java socket http_Java Socket Http

  • Post author:
  • Post category:java


用Java  Socket 实现发送HTTP请求和读取响应。

public class LearnHttp {

private static final byte CR = ‘\r’;

private static final byte LF = ‘\n’;

private static final byte[] CRLF = {CR, LF};

public static void main(String[] args) throws UnknownHostException, IOException {

new LearnHttp().testHttp();

}

public void testHttp() throws UnknownHostException, IOException {

String host = “www.baidu.com”;

Socket socket = new Socket(host, 80);

OutputStream out = socket.getOutputStream();

InputStream in = socket.getInputStream();

// 在同一个TCP连接里发送多个HTTP请求

for(int i = 0; i < 2; i++) {

writeRequest(out, host);

readResponse(in);

System.out.println(“\n\n\n”);

}

}

private void writeRequest(OutputStream out, String host) throws IOException {

// 请求行

out.write(“GET /index.html HTTP/1.1”.getBytes());

out.write(CRLF);// 请求头的每一行都是以CRLF结尾的

// 请求头

out.write((“Host: ” + host).getBytes()); // 此请求头必须

out.write(CRLF);

out.write(CRLF);// 单独的一行CRLF表示请求头的结束

// 可选的请求体。GET方法没有请求体

out.flush();

}

private void readResponse(InputStream in) throws IOException {

// 读取状态行

String statusLine = readStatusLine(in);

System.out.println(“statusLine :” + statusLine);

// 消息报头

Map headers = readHeaders(in);

int contentLength = Integer.valueOf(headers.get(“Content-Length”));

// 可选的响应正文

byte[] body = readResponseBody(in, contentLength);

String charset = headers.get(“Content-Type”);

if(charset.matches(“.+;charset=.+”)) {

charset = charset.split(“;”)[1].split(“=”)[1];

} else {

charset = “ISO-8859-1”;// 默认编码

}

System.out.println(“content:\n” + new String(body, charset));

}

private byte[] readResponseBody(InputStream in, int contentLength) throws IOException {

ByteArrayOutputStream buff = new ByteArrayOutputStream(contentLength);

int b;

int count = 0;

while(count++ < contentLength) {

b = in.read();

buff.write(b);

}

return buff.toByteArray();

}

private Map readHeaders(InputStream in) throws IOException {

Map headers = new HashMap();

String line;

while(!(“”.equals(line = readLine(in)))) {

String[] nv = line.split(“: “);// 头部字段的名值都是以(冒号+空格)分隔的

headers.put(nv[0], nv[1]);

}

return headers;

}

private String readStatusLine(InputStream in) throws IOException {

return readLine(in);

}

/**

* 读取以CRLF分隔的一行,返回结果不包含CRLF

*/

private String readLine(InputStream in) throws IOException {

int b;

ByteArrayOutputStream buff = new ByteArrayOutputStream();

while((b = in.read()) != CR) {

buff.write(b);

}

in.read();// 读取 LF

String line = buff.toString();

return line;

}

}

值得记下的教训:

InputStream.read()返回-1表示流的结束,即流被关闭了。在这次实现同一个Socket发送多个HTTP请求的过程中,一开始总是以为一个响应报文的结束是在InputStream.read返回-1时,导致第一次读取响应时总是很久才结束,第二次的请求虽然发出去了,但响应总是空的。

读取第一次请求的响应要很久是因为,这是在等待服务器关闭连接,百度的基本上是60秒。第二次是空的是因为,请求虽然发出去了,但服务器写响应的流已经被关闭,所以也读不到响应。

导致这个问题的根本原因在于没有记住:TCP是面向流的,并不知道传输的消息的开始和结束,这是由上层的协议去控制的,这里是HTTP协议。

欢迎关注我的微信公众号: coderbee笔记。



版权声明:本文为weixin_35705784原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。