编写HTTP代理中,上一些我的工具方法。

近来好多人亮相自已发出HTTP请求,我也正好也在做HTTP代理,还可以看看我写的一个文章也有关于HttpClient的内容
http://feixing2008.iteye.com/blog/569927

其中我没有使用HttpClient这个东西,我想更直接地透传数据。下边写了几个工具方法,引出内容。还有出遇到的一些问题。
	public static byte[] getDataByInputStream(InputStream in)
			throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] b = new byte[BUF_SIZE];
		int len = 0;
		while ((len = in.read(b, 0, BUF_SIZE)) != -1) {
			baos.write(b, 0, len);
		}
		baos.flush();
		byte[] bytes = baos.toByteArray();
		return bytes;
	}

上边的方法是从流中取出所有数据,放在数组中。BUF_SIZE常量自已设定,意义为缓冲区的大小。

在以上的基础上加增多一个方法
public static String getStrByInputStream(InputStream in, String outEncode) throws IOException{
		byte[] btyStr = getDataByInputStream(in);
		String str = new String(btyStr, outEncode);
		return str;
	}

这个方法主要是将响应的数据转成字符串。其中outEncode是字符器的编码。这里我要说一个问题
就是Content-Length这个头的主要含义是上边btye[]的长度,而不是String的length()的长度。
Content-Length反映了字节流的长度,这个受操作系统编码的影响。所以需要得到响应的编码形式。

继续再看看这个方法。
public static String getStrEveryLine(InputStream in, String encode)
			throws IOException {
		StringBuilder rs = new StringBuilder();
		Scanner inScn = new Scanner(in);
		String buf = "";
		while(inScn.hasNextLine()){
			buf = inScn.nextLine();
			if(buf.length() < 1){
				rs.append("\r\n");
				break;
			}
			rs.append(buf);
			rs.append("\r\n");
		}
		return rs.toString();

这个方法是是通过Scanner来处理流数据。本来使用getStrByInputStream这个方法好像已满足需求,其实非也,因为出现一个问题。

问题:
浏览器发出请求时,在完成请求头后关不会将socket关闭的,也就是说
in.read()是不会返回-1,只是线程会一直阻塞在那里。
再具体一点就是这样
GET / HTTP/1.0
Host: www.taobao.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: text/html
Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: t=31d7b3ed1734e98759c7009c7cc5d492; cna=G2ZHBE1o0hUCATjEb3H2nl0g
     <---------这里有个空行,读完这个空行再读的话会阻塞。所以读到这个空间就要跳出,不要再read


在请求头遇到这个情况还算好,因为可以通过一个空行可以发现头已经结束。
PS:不过这里还有个问题,如果是POST的话,在这个空行后边还是会有参数请求,这个时间就需要处理。然而我还没有实现。

在响应接收也是存在这个情况,这个问题更麻烦,因为是在响应数据流中读完一个字节流后阻塞,一般服务器是不会马上关闭Socket的,因为它不知你什么时间关闭,一般是等到10秒后才关闭。天吖,这个很影响我代理的性能。
其中一个解决方法是得到响应头的中Content-Length,再一次说明,这个头的值指的是字节流大小, 不是字符串大小。有了这个大小便可以根据大小读数据,知道何时数据已经完整,而不需要等服务器来关闭连接。

然而有时服务器是不会传Content-Length这个值。我也没有什么好方法,给合我代理的功能,我直接将流指向真实客服端的输出流当中。
BufferedInputStream bufIn = new BufferedInputStream(in);
		ArrayList<Byte> bys = new ArrayList<Byte>();
		BufferedOutputStream outBuf = new BufferedOutputStream(outToClient);
		int c = 0;
		int cCnt = 0;
		while(( c = bufIn.read()) != -1){
			++cCnt;
			byte bc = (byte)c;
			bys.add(bc);
			outBuf.write(c);
			if((cCnt % 256) == 0){
				outBuf.flush();
			}
		}
		outBuf.close();

这代码比较笨拙,其实不用细看,只是说明直接输到真实的客户端的输出流中。这个效率大大提升。

其中还有一个地方是线程池,这个是我需要改进的另一块,继续关注HTTP协议的相关开发。

你可能感兴趣的:(c,linux,socket,ubuntu,firefox)