写了一个简单的web后台,用socket接收并解析http请求。然而在网页发出第一次请求时总会卡住(其实是流阻塞了),刷新后可以访问。以及总是读取不到最后一部分数据。
刚开始是这样写的
private String getStringFromIOStream(InputStream ios){
BufferedReader reader = new BufferedReader( new InputStreamReader(ios));
StringBuilder ret = new StringBuilder();
String tstr=null;
while ((tstr=reader.readLine()) != null) {
ret.append(tstr).append("\n");
System.out.println(tstr);
}
return ret.toString();
}
查看线程信息发现是阻塞在了“tstr=reader.readLine()”这一行。得知是socket输入流读取信息阻塞了,可以理解阻塞在了信息最后一行。于是给socket对象添加超时,并处理异常,如下:
Socket client = tm.serverSocket.accept();
client.setSoTimeout(10); // 这里设置超时时长为10毫秒,对于读取http请求足够了
private String getStringFromIOStream(InputStream ios){
BufferedReader reader = new BufferedReader( new InputStreamReader(ios));
StringBuilder ret = new StringBuilder();
String tstr=null;
try {
while ((tstr=reader.readLine()) != null) {
ret.append(tstr).append("\n");
System.out.println(tstr);
}
}catch (IOException e){ // 读取完成时,超时会报异常,结束阻塞。
}
return ret.toString();
}
这是一旦超时便出发异常,从而解决了阻塞的问题。正在感觉完事大吉时,发现接收不到post请求发来的参数,而http请求,post的参数正是数据的最后一行。于是想到应该是http请求最后没有换行,所以readLine()阻塞在了最后一行数据上,而一旦发生异常,最后一行数据就拿不到了。想过添加参数,但发现添加了参数的Http请求,将所有参数封装在了同一行上,解决。于是换思路,考虑使用字节流解析来避免丢失最后一行数据的问题。代码如下
private String getStringFromIOStream(InputStream ios) {
int byteArrLen = 128;
byte[] bytes = new byte[byteArrLen];
StringBuilder ret = new StringBuilder();
int len = byteArrLen;
try{
while ((len=ios.read(bytes)) != -1 ){ // 当客户端主动中断连接时此处会返回-1
ret.append(new String(bytes,0,len)); // 在最后一次读取,len会小于数组大小,此处设置len避免末尾无效数据
System.out.println(new String(bytes,0,len));
if(len<byteArrLen){ // 获取不到预定大小说明可以结束
break;
}
}
}catch (IOException e){ // 超时结束
}
return ret.toString();
}
有4处细节需要注意,都写在上面备注里了。其中三处是结束循环,都不可少。