Java Socket发送与接收HTTP消息简单实现

                 

在上次Java Socket现实简单的HTTP服务我们实现了简单的HTTP服务,它可以用来模拟HTTP服务,用它可以截获HTTP请求的原始码流,让我们很清楚的了解到我们向服务发的HTTP消息的结构,对HTTP请求消息有个清晰的认识。这一节我想写了一个客户的程序,就是用来模拟浏览器,用来向服务器发送HTTP请求,最得要的是可以用它来显示服务器发回来的HTTP响应消息的一般结构。

import java.io.IOException;import java.io.InputStream;import java.io.OutputStreamWriter;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException;import java.util.ArrayList;/** * 一个简单的HTTP客户端,发送HTTP请求,模拟浏览器 * 可打印服务器发送过来的HTTP消息 */public class SimpleHttpClient private static String encoding = "GBK"public static void main(String[] args) {  try {   Socket s = new Socket(InetAddress.getLocalHost(), 8080);   OutputStreamWriter osw = new OutputStreamWriter(s.getOutputStream());   StringBuffer sb = new StringBuffer();   sb.append("GET /HttpStream/gb2312.jsp HTTP/1.1\r\n");   sb.append("Host: localhost:8088\r\n");   sb.append("Connection: Keep-Alive\r\n");   //注,这是关键的关键,忘了这里让我搞了半个小时。这里一定要一个回车换行,表示消息头完,不然服务器会等待   sb.append("\r\n");   osw.write(sb.toString());   osw.flush();   //--输出服务器传回的消息的头信息   InputStream is = s.getInputStream();   String line = null;   int contentLength = 0;//服务器发送回来的消息长度   // 读取所有服务器发送过来的请求参数头部信息   do {    line = readLine(is, 0);    //如果有Content-Length消息头时取出    if (line.startsWith("Content-Length")) {     contentLength = Integer.parseInt(line.split(":")[1].trim());    }    //打印请求部信息    System.out.print(line);    //如果遇到了一个单独的回车换行,则表示请求头结束   } while (!line.equals("\r\n"));   //--输消息的体   System.out.print(readLine(is, contentLength));   //关闭流   is.close();  } catch (UnknownHostException e) {   e.printStackTrace();  } catch (IOException e) {   e.printStackTrace();  } } /*  * 这里我们自己模拟读取一行,因为如果使用API中的BufferedReader时,它是读取到一个回车换行后  * 才返回,否则如果没有读取,则一直阻塞,直接服务器超时自动关闭为止,如果此时还使用BufferedReader  * 来读时,因为读到最后一行时,最后一行后不会有回车换行符,所以就会等待。如果使用服务器发送回来的  * 消息头里的Content-Length来截取消息体,这样就不会阻塞  *   * contentLe 参数 如果为0时,表示读头,读时我们还是一行一行的返回;如果不为0,表示读消息体,  * 时我们根据消息体的长度来读完消息体后,客户端自动关闭流,这样不用先到服务器超时来关闭。  */ private static String readLine(InputStream is, int contentLe) throws IOException {  ArrayList lineByteList = new ArrayList();  byte readByte;  int total = 0;  if (contentLe != 0) {   do {    readByte = (byte) is.read();    lineByteList.add(Byte.valueOf(readByte));    total++;   } while (total < contentLe);//消息体读还未读完  } else {   do {    readByte = (byte) is.read();    lineByteList.add(Byte.valueOf(readByte));   } while (readByte != 10);  }  byte[] tmpByteArr = new byte[lineByteList.size()];  for (int i = 0; i < lineByteList.size(); i++) {   tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();  }  lineByteList.clear();  return new String(tmpByteArr, encoding); }}


 

运行时访问一个页面打印如下:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=61F659691475622CE7AB9C84E7AE7273; Path=/HttpStream
Content-Type: text/html;charset=GB2312
Content-Length: 81
Date: Mon, 09 Nov 2009 13:15:23 GMT

  
<html>  
    <body>  
  你好,这是一个简单的测试
    </body> 
</html>

下面来个文件下载的看怎么样?

请求的Jsp页面如下:

<%@page import="java.io.InputStream" contentType="text/html; charset=GB2312"%><%@page import="java.io.FileInputStream"%><%@page import="java.io.OutputStream"%><html> <body> <br>  <%   try {    InputStream is = new FileInputStream("e:/tmp/file2.txt");    OutputStream os = response.getOutputStream();    byte[] readContent = new byte[1024];    int readCount = 0;    while (is.available() > 0) {     readCount = is.read(readContent);     os.write(readContent, 0, readCount);    }        is.close();    //注这里一定要关闭,不然的话抛异常,异常请见下面,原因就是response.getWriter()    //与response.getOutputStream()不能同时使用,如果在这里关闭了,前面与后面向    //out对象里写的数据就不会刷新到客户端了,只有向response.getOutputStream()写的    //数据会输出到客户端。    os.close();   } catch (Exception e) {    e.printStackTrace();   }  %> </body></html>


 

如里上面Jsp下载页面中的 os.close() 注释掉的话会抛如下异常:

exception

org.apache.jasper.JasperException: getOutputStream() has already been called for this response org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:476) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:383) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:315) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265) javax.servlet.http.HttpServlet.service(HttpServlet.java:803)

root cause

java.lang.IllegalStateException: getOutputStream() has already been called for this response org.apache.catalina.connector.Response.getWriter(Response.java:601) org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:196) org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125) org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118) org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:185) org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:116) org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:76) org.apache.jsp.gb2312_jsp._jspService(gb2312_jsp.java:78) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98) javax.servlet.http.HttpServlet.service(HttpServlet.java:803) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:328) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:315) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265) javax.servlet.http.HttpServlet.service(HttpServlet.java:803)

以下是服务器经过编译生成的servlet类文件:

package org.apache.jsp;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;import java.io.InputStream;import java.io.FileInputStream;import java.io.OutputStream;public final class gb2312_jsp extends org.apache.jasper.runtime.HttpJspBase    implements org.apache.jasper.runtime.JspSourceDependent private static java.util.List _jspx_dependants;  public Object getDependants() {    return _jspx_dependants;  }  public void _jspService(HttpServletRequest request, HttpServletResponse response)        throws java.io.IOException, ServletException {    JspFactory _jspxFactory = null;    PageContext pageContext = null;    HttpSession session = null;    ServletContext application = null;    ServletConfig config = null;    JspWriter out = null;    Object page = this;    JspWriter _jspx_out = null;    PageContext _jspx_page_context = null;    try {      _jspxFactory = JspFactory.getDefaultFactory();      response.setContentType("text/html; charset=GB2312");      pageContext = _jspxFactory.getPageContext(this, request, response,         null, true, 8192, true);      _jspx_page_context = pageContext;      application = pageContext.getServletContext();      config = pageContext.getServletConfig();      session = pageContext.getSession();      out = pageContext.getOut();      _jspx_out = out;      out.write("\r\n");      out.write("\r\n");      out.write("\r\n");      out.write("<html>\r\n");      out.write("\t<body> <br>\r\n");      out.write("\t\t");   try {    InputStream is = new FileInputStream("e:/tmp/file2.txt");    OutputStream os = response.getOutputStream();    byte[] readContent = new byte[1024];    int readCount = 0;    while (is.available() > 0) {     readCount = is.read(readContent);     os.write(readContent, 0, readCount);    }        is.close();    //注这里一定要关闭,不然的话抛异常,异常请见下面,原因就是response.getWriter()    //与response.getOutputStream()不能同时使用,如果在这里关闭了,前面与后面向    //out对象里写的数据就不会刷新到客户端了,只有向response.getOutputStream()写的    //数据会输出到客户端。    os.close();   } catch (Exception e) {    e.printStackTrace();   }        out.write("\r\n");      out.write("\t</body>\r\n");      out.write("</html>");    } catch (Throwable t) {      if (!(t instanceof SkipPageException)){        out = _jspx_out;        if (out != null && out.getBufferSize() != 0)          out.clearBuffer();        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);      }    } finally {      if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);    }  }}


 

最后是服务向客户端输出的码流如下:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=328097D70C625E8A9279FF9472319A5D; Path=/HttpStream
Content-Type: text/html;charset=GB2312
Content-Length: 60
Date: Mon, 09 Nov 2009 13:19:22 GMT

这是测试文件的内容:
中a
~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

猜你喜欢

转载自blog.csdn.net/qq_43685588/article/details/87295999