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

    转载自:http://jiangzhengjun.iteye.com/blog/513217


  • 相关阅读:
    Web APP开发技巧总结
    2015年 移动开发都有哪些热点?
    手机APP UI设计尺寸基础知识
    最新Android & iOS设计尺寸规范
    给iOS开发者的Android开发建议
    Android 应用开发推荐书单
    Android 你应该知道的学习资源 进阶之路贵在坚持
    一篇文章让你读懂iOS和Android的历史起源
    Android平台上最好的几款免费的代码编辑器
    Google 发布的15个 Android 性能优化典范
  • 原文地址:https://www.cnblogs.com/xieyuan/p/3787468.html
Copyright © 2011-2022 走看看