zoukankan      html  css  js  c++  java
  • Java Socket发送与接收HTTP消息简单实现

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

    1. import java.io.IOException;  
    2. import java.io.InputStream;  
    3. import java.io.OutputStreamWriter;  
    4. import java.net.InetAddress;  
    5. import java.net.Socket;  
    6. import java.net.UnknownHostException;  
    7. import java.util.ArrayList;  
    8.   
    9. /** 
    10.  * 一个简单的HTTP客户端,发送HTTP请求,模拟浏览器 
    11.  * 可打印服务器发送过来的HTTP消息 
    12.  */  
    13. public class SimpleHttpClient {  
    14.     private static String encoding = "GBK";  
    15.   
    16.     public static void main(String[] args) {  
    17.         try {  
    18.             Socket s = new Socket(InetAddress.getLocalHost(), 8080);  
    19.             OutputStreamWriter osw = new OutputStreamWriter(s.getOutputStream());  
    20.             StringBuffer sb = new StringBuffer();  
    21.             sb.append("GET /HttpStream/gb2312.jsp HTTP/1.1 ");  
    22.             sb.append("Host: localhost:8088 ");  
    23.             sb.append("Connection: Keep-Alive ");  
    24.             //注,这是关键的关键,忘了这里让我搞了半个小时。这里一定要一个回车换行,表示消息头完,不然服务器会等待  
    25.             sb.append(" ");  
    26.             osw.write(sb.toString());  
    27.             osw.flush();  
    28.   
    29.             //--输出服务器传回的消息的头信息  
    30.             InputStream is = s.getInputStream();  
    31.             String line = null;  
    32.             int contentLength = 0;//服务器发送回来的消息长度  
    33.             // 读取所有服务器发送过来的请求参数头部信息  
    34.             do {  
    35.                 line = readLine(is, 0);  
    36.                 //如果有Content-Length消息头时取出  
    37.                 if (line.startsWith("Content-Length")) {  
    38.                     contentLength = Integer.parseInt(line.split(":")[1].trim());  
    39.                 }  
    40.                 //打印请求部信息  
    41.                 System.out.print(line);  
    42.                 //如果遇到了一个单独的回车换行,则表示请求头结束  
    43.             } while (!line.equals(" "));  
    44.   
    45.             //--输消息的体  
    46.             System.out.print(readLine(is, contentLength));  
    47.   
    48.             //关闭流  
    49.             is.close();  
    50.   
    51.         } catch (UnknownHostException e) {  
    52.             e.printStackTrace();  
    53.         } catch (IOException e) {  
    54.             e.printStackTrace();  
    55.         }  
    56.     }  
    57.   
    58.     /* 
    59.      * 这里我们自己模拟读取一行,因为如果使用API中的BufferedReader时,它是读取到一个回车换行后 
    60.      * 才返回,否则如果没有读取,则一直阻塞,直接服务器超时自动关闭为止,如果此时还使用BufferedReader 
    61.      * 来读时,因为读到最后一行时,最后一行后不会有回车换行符,所以就会等待。如果使用服务器发送回来的 
    62.      * 消息头里的Content-Length来截取消息体,这样就不会阻塞 
    63.      *  
    64.      * contentLe 参数 如果为0时,表示读头,读时我们还是一行一行的返回;如果不为0,表示读消息体, 
    65.      * 时我们根据消息体的长度来读完消息体后,客户端自动关闭流,这样不用先到服务器超时来关闭。 
    66.      */  
    67.     private static String readLine(InputStream is, int contentLe) throws IOException {  
    68.         ArrayList lineByteList = new ArrayList();  
    69.         byte readByte;  
    70.         int total = 0;  
    71.         if (contentLe != 0) {  
    72.             do {  
    73.                 readByte = (byte) is.read();  
    74.                 lineByteList.add(Byte.valueOf(readByte));  
    75.                 total++;  
    76.             } while (total < contentLe);//消息体读还未读完  
    77.         } else {  
    78.             do {  
    79.                 readByte = (byte) is.read();  
    80.                 lineByteList.add(Byte.valueOf(readByte));  
    81.             } while (readByte != 10);  
    82.         }  
    83.   
    84.         byte[] tmpByteArr = new byte[lineByteList.size()];  
    85.         for (int i = 0; i < lineByteList.size(); i++) {  
    86.             tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();  
    87.         }  
    88.         lineByteList.clear();  
    89.   
    90.         return new String(tmpByteArr, encoding);  
    91.     }  
    92. }  


     

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

    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页面如下:

    1. <%@page import="java.io.InputStream" contentType="text/html; charset=GB2312"%>  
    2. <%@page import="java.io.FileInputStream"%>  
    3.   
    4. <%@page import="java.io.OutputStream"%><html>  
    5.     <body> <br>  
    6.         <%  
    7.             try {  
    8.                 InputStream is = new FileInputStream("e:/tmp/file2.txt");  
    9.                 OutputStream os = response.getOutputStream();  
    10.                 byte[] readContent = new byte[1024];  
    11.                 int readCount = 0;  
    12.                 while (is.available() > 0) {  
    13.                     readCount = is.read(readContent);  
    14.                     os.write(readContent, 0, readCount);  
    15.                 }  
    16.                   
    17.                 is.close();  
    18.                 //注这里一定要关闭,不然的话抛异常,异常请见下面,原因就是response.getWriter()  
    19.                 //与response.getOutputStream()不能同时使用,如果在这里关闭了,前面与后面向  
    20.                 //out对象里写的数据就不会刷新到客户端了,只有向response.getOutputStream()写的  
    21.                 //数据会输出到客户端。  
    22.                 os.close();  
    23.             } catch (Exception e) {  
    24.                 e.printStackTrace();  
    25.             }  
    26.         %>  
    27.     </body>  
    28. </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类文件:

    1. package org.apache.jsp;  
    2.   
    3. import javax.servlet.*;  
    4. import javax.servlet.http.*;  
    5. import javax.servlet.jsp.*;  
    6. import java.io.InputStream;  
    7. import java.io.FileInputStream;  
    8. import java.io.OutputStream;  
    9.   
    10. public final class gb2312_jsp extends org.apache.jasper.runtime.HttpJspBase  
    11.     implements org.apache.jasper.runtime.JspSourceDependent {  
    12.   
    13.   private static java.util.List _jspx_dependants;  
    14.   
    15.   public Object getDependants() {  
    16.     return _jspx_dependants;  
    17.   }  
    18.   
    19.   public void _jspService(HttpServletRequest request, HttpServletResponse response)  
    20.         throws java.io.IOException, ServletException {  
    21.   
    22.     JspFactory _jspxFactory = null;  
    23.     PageContext pageContext = null;  
    24.     HttpSession session = null;  
    25.     ServletContext application = null;  
    26.     ServletConfig config = null;  
    27.     JspWriter out = null;  
    28.     Object page = this;  
    29.     JspWriter _jspx_out = null;  
    30.     PageContext _jspx_page_context = null;  
    31.   
    32.   
    33.     try {  
    34.       _jspxFactory = JspFactory.getDefaultFactory();  
    35.       response.setContentType("text/html; charset=GB2312");  
    36.       pageContext = _jspxFactory.getPageContext(this, request, response,  
    37.                 null, true, 8192, true);  
    38.       _jspx_page_context = pageContext;  
    39.       application = pageContext.getServletContext();  
    40.       config = pageContext.getServletConfig();  
    41.       session = pageContext.getSession();  
    42.       out = pageContext.getOut();  
    43.       _jspx_out = out;  
    44.   
    45.       out.write(" ");  
    46.       out.write(" ");  
    47.       out.write(" ");  
    48.       out.write("<html> ");  
    49.       out.write(" <body> <br> ");  
    50.       out.write(" ");  
    51.   
    52.             try {  
    53.                 InputStream is = new FileInputStream("e:/tmp/file2.txt");  
    54.                 OutputStream os = response.getOutputStream();  
    55.                 byte[] readContent = new byte[1024];  
    56.                 int readCount = 0;  
    57.                 while (is.available() > 0) {  
    58.                     readCount = is.read(readContent);  
    59.                     os.write(readContent, 0, readCount);  
    60.                 }  
    61.                   
    62.                 is.close();  
    63.                 //注这里一定要关闭,不然的话抛异常,异常请见下面,原因就是response.getWriter()  
    64.                 //与response.getOutputStream()不能同时使用,如果在这里关闭了,前面与后面向  
    65.                 //out对象里写的数据就不会刷新到客户端了,只有向response.getOutputStream()写的  
    66.                 //数据会输出到客户端。  
    67.                 os.close();  
    68.             } catch (Exception e) {  
    69.                 e.printStackTrace();  
    70.             }  
    71.           
    72.       out.write(" ");  
    73.       out.write(" </body> ");  
    74.       out.write("</html>");  
    75.     } catch (Throwable t) {  
    76.       if (!(t instanceof SkipPageException)){  
    77.         out = _jspx_out;  
    78.         if (out != null && out.getBufferSize() != 0)  
    79.           out.clearBuffer();  
    80.         if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);  
    81.       }  
    82.     } finally {  
    83.       if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);  
    84.     }  
    85.   }  
    86. }  


     

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

    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 ~!@#$%^&*()_+{}|:" <>?`-=[]\;',./

    原文:http://blog.csdn.net/a9529lty/article/details/7174265

  • 相关阅读:
    AVA 源码解读-规则逻辑
    gojs 如何实现虚线(蚂蚁线)动画?
    谷歌复制接口json
    vue拼图动画Demo
    如何获取一个类的全名
    `webpack-dev-server --inline --progress --config build/webpack.dev.conf.js(vue报错)
    Jquery通知组件
    字典树 (Trie Tree)
    [CPP] 虚函数与纯虚函数
    格雷码
  • 原文地址:https://www.cnblogs.com/langtianya/p/4329081.html
Copyright © 2011-2022 走看看