您的位置:首页 > 理论基础 > 计算机网络

Java,InputStream,Socket阻塞.(关于HTTP请求的IO问题自我总结)

2016-12-08 17:23 447 查看

前言:

由于项目的需求,需要实现以下流程:
1. Client发送HTTP请求到Server.
2. Server接收HTTP请求并显示出请求的内容(包含请求的头及Content的内容)

服务端实现: 

Server部分代码如下:
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

/**
*
* @author Anson
*/
public class Server{

private String ServerIP;
private int ServerPort;
private boolean shutdown = false;

/**
* Init Server
*/
public void init(){

ServerIP = "192.168.0.1";
ServerPort = 8082;
}

/**
*
*/
public void await(){

ServerSocket serverSocket = null;

try {
serverSocket =  new ServerSocket(ServerPort, 1, InetAddress.getByName(ServerIP));
}catch (IOException e) {
e.printStackTrace();
System.exit(1);
}

// Loop waiting for a request
while(!shutdown){
Socket          socket  = null;
InputStream     input   = null;
OutputStream    output  = null;

try {
socket  = serverSocket.accept();
input   = socket.getInputStream();
output  = socket.getOutputStream();

this.parse(input);

// Close the socket
socket.close();

}catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public void parse(InputStream input) {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer[j]);
}
System.out.println(request.toString());
}

}

 再编写StartServer.java
....
public class StartServer{
public static void main(String[] args){
Server server = new Server();
server.init();
server.await();
.....
}
}

跟着,客户端的代码做法有很多,
可以用最简单的Socket,
也可以用HttpClient,
或其它客户端如:OC
具体做法,网上有很多,在此不细述.
我用的是OC,
当然,可能有些做法,并不会出现这样的问题,
毕竟,我,并非一个高手.
我只描述我遇到的状况,
成功实现的可以忽略.
 

问题描述:

当客户端发送的数据量小于一定长度(如2048byte)的时候,
Server运行正常,即可以正常打印出请求的内容.
如:GET index HTTP/1.1
    ........
请求的头加上一些Content.
但当数据量大于2048的时候,奇怪的问题出现了.
有时会发现数据读不全.
例如,在请求的头下面,Content的内容是一串XML的String:
<?xml version="1.0" encoding="UTF-8" ?><label1>label1></label1>......
当请求的头加上XML的String的总长度超过2048,那么,可能出现在情况就是超过的部分丢失.

解决办法:

可能已经有人发觉,在上面的Server里面的parse(InputStream input)这里的处理有问题.
因为很大程度上这个2048在StringBuffer的定义时就出现了.
所以,第一个失败的尝试便是修改2048.
第一次变成10*2048
跟着是100*2048....
最后是1000*2048....
但结果可以发现:数据量的限制并不会随着这个数值的成倍增长而以相同倍数增长.有时可能是为了增加一倍,
而这个数值必需增加20倍.....
最后发现这个办法不可能,
于是,改变了读取的办法.
用了其它的如StreamBufferReader还有很多其它的来尝试读取,
结果却令人失望,
甚至于这时候出现了阻塞.
即,在读取的过停中,卡住了,
直到等到客户端的请求超时,才跳出来.
所以这些方法也失败了.
具体的情况也记不大清楚了.
 
之后的另一个方法:
         String request = "";
           //如果不先读一次,那么下面的input.available()有可能是0值.
           char firstbyte = (char) input.read();
            request += firstbyte;
            int ava = 0;
            while ((ava = input.available()) > 0) {
                try {
                    // 在此睡眠0.1秒,很重要
                    Thread.sleep(100);
                } catch (Exception t) {
                    t.printStackTrace();
                }
                byte[] bufferedbyte = new byte[ava];
                input.read(bufferedbyte, 0, ava);
                request += new String(bufferedbyte, "UTF-8");
            }
 这是一个成功解决的方法,
 之所以要睡眠0.1秒,等待高手帮忙解答,
这也许跟网络有关系,
当然,数据量越大,睡眠的时应该有小小的加长.(缓冲时间).
虽然以上的做法成功实现了,但对于服务器来说,效率是个问题,所以只能寻找更优的办法.
 
第三次更改:
 
int readInt = 0;
int availableCount = 0;
char requestHead = (char)(input.read());
request += requestHead;

//取得请求的类型是PUT或GET
if(requestHead == 'P') {//主要是PUT的方法里面会带有XML.
while((readInt=input.read()) != -1) {
request += (char)readInt;
if(request.indexOf("</endXML>") != -1){
break;
}
}
System.out.println("this is put\n" +request);
} else if(requestHead=='G') {//GET的方法内容比较少,用以下的方法可以正常实现
while((availableCount=input.available()) > 0){
byte[] requestbuffer = new byte[availableCount];
input.read(requestbuffer);
request += new String(requestbuffer, "UTF-8");
} 

*代码并不完整.
这种做法,我在读取XML的String里加上了一个结束的判断即对"</endXML>"的判断(当然,这是在知道Content内容的基础上这种做法才可行).
虽然暂时解决了问题,但仍然不能完美解决存在的问题.
 
第四次更改:
这也是最后一次更改,办法是:
在PUT的请求里面,先取得一个Content-Length:的请求头的值length.
这里的大小就是Content的长度.
在这个基础上,
先判断是否开始读取Content:
if(requestString.indexOf("\r\n\r\n") != -1)
.....
 
之后再循环读取:
    for(int i=0; i < length; i++){
        requestString += (char)input.read();
    }
当读完length后,跳出循环,
并跳出读取input的读取.
.......
问题在此告一段落.
 
若有哪位朋友懂得其中原理,恳请告知,
知其然而不知其所以然,心里不免有个结.

结言:

以上做法,仅供参考.
若有错误,请不啬指出.
 
欢迎转载
转载进请注明转自:http://ansonlai.iteye.com
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: