Java BlackBerry OS 5上的网络I/O挂起
我在黑莓操作系统5上的网络I/O代码有一些问题 在I/O操作过程中,我不断地遇到零星挂起,最终出现TCP超时异常 我使用5.0网络API建立连接,每次都能完美地工作 问题是在执行实际I/O时。我有一个后台工作线程,为队列中的I/O请求提供服务。只有一个后台线程,因此所有请求都序列化到此线程上 完成通知通过委托接口完成,委托接口在请求排队时传入 在后台工作线程上调用完成委托,但是客户端可以通过Java BlackBerry OS 5上的网络I/O挂起,java,blackberry,inputstream,java-me,Java,Blackberry,Inputstream,Java Me,我在黑莓操作系统5上的网络I/O代码有一些问题 在I/O操作过程中,我不断地遇到零星挂起,最终出现TCP超时异常 我使用5.0网络API建立连接,每次都能完美地工作 问题是在执行实际I/O时。我有一个后台工作线程,为队列中的I/O请求提供服务。只有一个后台线程,因此所有请求都序列化到此线程上 完成通知通过委托接口完成,委托接口在请求排队时传入 在后台工作线程上调用完成委托,但是客户端可以通过invokeLater将其重新发布到事件线程,以进行UI更新等 备注: HttpRequest是我自己的类
invokeLater
将其重新发布到事件线程,以进行UI更新等
备注:HttpRequest是我自己的类,它保存有关请求的数据。
MutableData是我自己的类,它保存读取的数据。
缓冲区大小=2048
HttpConnection getConnectionForRequest(final HttpRequest inRequest) {
final String url = inRequest.getURL();
final int[] availableTransportTypes =
TransportInfo.getAvailableTransportTypes();
final ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setPreferredTransportTypes(availableTransportTypes);
connectionFactory.setConnectionMode(ConnectionFactory.ACCESS_READ);
final ConnectionDescriptor connectionDescriptor =
connectionFactory.getConnection(url);
HttpConnection connection = null;
if (connectionDescriptor != null) {
connection = (HttpConnection) connectionDescriptor.getConnection();
}
return connection;
}
public void run() {
while (isRunning()) {
// This blocks waiting on a request to appear in the queue.
final HttpRequest request = waitForRequest();
final HttpConnection connection = getConnectionForRequest(request);
final MutableData data = new MutableData();
final InputStream inputStream = connection.openInputStream();
final byte[] readBuffer = new byte[BUFFER_SIZE];
int chunkSize;
// *** The following read call sporadically hangs and eventually throws
// a TCP timeout exception.
while((chunkSize = inputStream.read(readBuffer, 0, BUFFER_SIZE)) != -1) {
data.appendData(readBuffer, 0, chunkSize);
}
mDelegate.receivedDataForRequest(request, data);
}
}
当它挂起时,它总是在大约30秒后抛出TCP超时错误。
如果这种情况偶尔发生,我会把它归为正常的网络拥塞,但它不会发生
频繁发生足以表明存在更深层次的问题
编辑:
它发生在各种模拟器和我拥有的两个物理设备上。
我试过的模拟器是
- 风暴9550
- 旅游9630
- Bold 9000
- 珍珠9100
- 曲线8530
任何帮助都将不胜感激。您可能想试试这种方法。即使您在一个反向线程上序列化数据,看起来请求是在主线程中创建的。您可能会遇到一些奇怪的竞争情况。您是否可以添加一些日志来显示设备选择用于每个连接的传输类型?可能是传输选择API选择了一个它认为可以工作的传输,而实际上它不能工作。有人建议在我的网络I/O线程中放置一个暂停检测器,当检测到暂停时,中断线程并重新启动请求。我通过在开始请求之前启动一个计时器来实现这一点,当我读取每个数据块时,我会重置计时器。如果计时器在我读取数据块之前过期,我假设网络已经停止,我中断线程并重新开始该请求 我已经这样做了,它确实改善了一些事情,至少减少了我在继续请求之前必须等待的延迟,因为我不必等待TCP超时,这可能需要很长时间 中断当前的I/O操作并重新启动似乎可以使网络恢复正常运行一段时间,通常在再次停止之前可以正常运行几分钟。在调试时,我将暂停记录到控制台,并且我得到了相当多的暂停
这是一个非常奇怪的问题,我对失速检测解决方案并不完全满意。这似乎只是掩盖了问题,但它确实让我在某种程度上解决了我遇到的长时间延迟问题。我认为在我使用过的所有BlackBerry操作系统-4.5到6.0上执行
read(byte[],int,int)
时存在缺陷。我为InputStream编写了一个适配器,它将
read(byte[],int,int)
转换为对read()
的单个调用,从而解决了我正在开发的应用程序中的流挂起问题
如果您阅读了read(byte[],int,int)的RIM规范
InputStream类的read(b,off,len)方法只是重复调用read()方法。如果第一个这样的调用导致IOException,那么从对read(b,off,len)方法的调用返回该异常。如果对read()的任何后续调用导致IOException,则会捕获该异常并将其视为文件结尾;读取到该点的字节存储到b中,并返回异常发生前读取的字节数。鼓励子类提供此方法更有效的实现
我按照这个规范编写了自己的版本,遇到了同样的问题。我认为问题在于,一旦有数据可用,该方法需要返回而不阻塞。唯一的方法是利用available()
查看在不阻塞的情况下可以读取多少字节。由于RIM文档没有提到使用available()
,我认为它只是调用read()
,直到缓冲区满了或者read()返回-1。如果您的数据以小的突发方式出现,这可能需要很长时间。如果“长时间”超过了连接超时时间,那么连接就会终止
下面是我使用的代码,它解决了挂起连接问题:
public int read(byte[] bts, int st, int len) throws IOException {
if(len == 0) {
return 0;
}
int readByte = this.read();
if(readByte == -1) {
return 0;
}
bts[st] = (byte)readByte;
return 1;
}
这是在模拟器中还是在真实设备中?添加了设备和模拟器信息。我问这个问题的原因是因为我发现5.0模拟器在网络仿真方面有很多缺陷-通常没有明显的原因而挂起连接。然而,既然你在真实的设备上也看到了它,那一定是不一样的。每次它选择TRANSPORT\u TCP\u CELLULAR时。我还以为我可能会遇到一个奇怪的竞争条件,但就我的一生而言,我找不到一个。唯一的共享数据是请求队列,它在发布线程和处理线程之间正确同步。