`

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

    博客分类:
  • Java
阅读更多

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

分享到:
评论
1 楼 pangtuxiangqian 2011-12-12  
非常感谢,参考了你的博文,想通了这方面的疑问,再次感谢!

相关推荐

    java使用Socket类接收和发送数据

    总的来说,Java的Socket类是实现网络通信的基础,通过创建Socket实例,我们可以与远程服务器建立连接,进行数据的发送和接收。了解和熟练掌握Socket类的使用,对于编写任何基于TCP协议的网络应用程序都是至关重要的...

    Java Socket发送和接收的例子

    这个简单的例子展示了Java Socket基本的发送和接收机制,但实际的网络应用可能会更复杂,包括多线程处理多个客户端、错误处理、数据编码和解码等。 总结来说,Java Socket编程是构建网络应用程序的关键技术,它允许...

    利用socket连续发送与连续接收数据

    本文将深入探讨如何利用Socket进行连续发送与连续接收数据,基于标题“利用socket连续发送与连续接收数据”以及描述中提到的实现过程。 首先,让我们理解什么是Socket。Socket是进程间通信的一种方式,它提供了一种...

    Java Socket Email 发送接收

    Java Socket电子邮件发送与接收是基于Java网络编程技术实现的一种通信方式,主要涉及到Java的Socket类以及Email相关的API。本项目是一个课程项目,开发者通过几天的努力完成了一个具备图形用户界面(GUI)的邮件系统...

    java基于socket的文件发送与接收,有界面,有进度条

    Java基于Socket的文件发送与接收是一个常见的网络编程任务,它涉及到客户端和服务器之间的通信,以及大数据量的传输。在本项目中,开发者为用户提供了一个图形用户界面(GUI),并且在传输过程中显示了进度条,这...

    JAVA Socket编程实现文件上传

    在这个场景中,我们讨论的是如何使用Java的Socket来实现文件上传功能,即从客户端将文件发送到服务器,然后保存到服务器的数据库中。这个过程涉及到多个关键知识点,下面我们将详细探讨。 1. **Java Socket基础**:...

    Java基于socket实现的客户端和服务端通信功能完整实例

    Java基于socket实现的客户端和服务端通信功能完整实例 在Java中,Socket是实现网络通信的基础,通过Socket可以实现客户端和服务器端之间的通信。本文将详细介绍Java基于Socket实现的客户端和服务端通信功能,包括...

    java socket 客户端代码

    在Java编程语言中,Socket是实现网络通信的基础组件,它为两台计算机之间的通信提供了低级别的接口。在本文中,我们将深入探讨Java Socket客户端代码及其在创建基于Socket的聊天室客户端中的应用。 首先,理解...

    Socket发送并接收服务器返回的数据

    本文将深入探讨如何在Android平台上利用Socket进行数据的发送与接收,以及服务器返回数据的处理。 首先,理解Socket的基本概念:Socket,又称为“套接字”,是网络编程中的一个接口,它提供了进程间通信(IPC,...

    java socket 实现SMTP协议 发送邮件.docx

    Java Socket 实现 SMTP 协议发送邮件 Java Socket 是 Java 语言中用于实现网络编程的 API,通过 Socket,可以实现与远程服务器的通信。在这里,我们将使用 Java Socket 实现 SMTP 协议来发送邮件。 SMTP 协议简介 ...

    java socket 多线程 简易实现

    Java Socket多线程简易实现是网络编程中的一个基础概念,主要应用于客户端与服务器之间的通信。在Java中,Socket是基于TCP协议的,提供了一种可靠的、面向连接的字节流通信方式。多线程则是Java并发编程的重要特性,...

    通过java socket实现屏幕网络监控

    实现手机控制电脑端的部分,一般会涉及到移动设备的触摸事件转化为键盘鼠标事件模拟,这通常需要额外的协议设计和实现,例如将触摸事件转换为特定的控制指令,然后通过Socket发送到服务器,服务器再模拟这些操作。...

    java socket通信自定义消息协议

    本篇将详细讲解如何在Java中实现基于Socket的自定义消息协议,以及客户端与服务器端的数据转换与解析过程。 首先,我们来看`SocketClient.java`,这是客户端的代码实现。客户端通常需要以下几个步骤: 1. 创建...

    Java Socket 聊天通信演示代码

    Java Socket 是一种网络通信协议,它是Java编程语言中实现客户端-服务器模型的基础。Socket 提供了低级别的、面向连接的、双向通信的网络接口,允许应用程序通过网络进行数据传输。在本示例中,我们将深入探讨Java ...

    Socket服务端客户端以tcp/ip协议发送接收报文

    本教程将详细讲解如何利用Java Socket实现基于TCP/IP的服务端和客户端之间的报文发送与接收。 TCP(传输控制协议)是一种面向连接、可靠的传输协议,它确保了数据包的有序和无损传输。IP(互联网协议)则是互联网上...

    java socket 视频流转发socket

    Java Socket是Java编程语言中用于网络通信的核心API,它提供了低级别的、面向连接的、基于TCP/IP协议的网络通信能力。在"Java Socket 视频流转发Socket"这个主题中,我们将深入探讨如何使用Java Socket来处理视频流...

    利用socket发送接收邮件(转)

    在IT行业中,网络通信是不可或缺...总的来说,利用Socket发送和接收邮件涉及到网络编程、邮件协议、Java的I/O和特定的Java邮件API。这不仅有助于开发自定义的邮件客户端,也有助于理解电子邮件在网络传输中的工作原理。

    java socket实现双机通信 有界面

    一旦连接建立,客户端也可以通过这个Socket发送和接收消息。 在GUI方面,我们可以使用Java的Swing或JavaFX库来创建聊天界面。用户可以通过文本框输入消息,点击发送按钮后,这些消息将通过Socket发送到服务器;同时...

    java socket 大文件传输,快速传输(包的分片,组装)源码

    在大文件传输中,Socket用于建立客户端与服务器之间的连接,并负责数据的发送和接收。 - **ServerSocket**:服务器端使用ServerSocket监听特定端口,等待客户端的连接请求。 4. **NIO(Non-blocking I/O)** - **...

    java Socket通信实现

    Java Socket通信实现是Java网络编程中的重要组成部分,它允许两台计算机通过TCP/IP协议进行双向通信。在Java中,Socket提供了低级别的、基于连接的、面向数据流的通信API,可以用于实现客户端-服务器架构的应用程序...

Global site tag (gtag.js) - Google Analytics