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

前言:

由于项目的需求,需要实现以下流程:

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

 

你可能感兴趣的:(java,应用服务器,xml,socket,网络应用)