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

  • 相关阅读:
    Codeforces 877 C. Slava and tanks
    Codeforces 877 D. Olya and Energy Drinks
    2017 10.25 NOIP模拟赛
    2017 国庆湖南 Day1
    UVA 12113 Overlapping Squares
    学大伟业 国庆Day2
    51nod 1629 B君的圆锥
    51nod 1381 硬币游戏
    [JSOI2010]满汉全席
    学大伟业 2017 国庆 Day1
  • 原文地址:https://www.cnblogs.com/langtianya/p/4329081.html
Copyright © 2011-2022 走看看