你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 跟高手学编程
1.2 用Java实现非阻塞的HTTP服务器(上)
 

一、HTTP协议简介

当用户打开浏览器,输入一个URL地址,就能接收到远程HTTP服务器发送过来的网页。浏览器就是常见的HTTP客户程序。如图1所示,HTTP客户程序必须先发出一个HTTP请求,然后才能接收到来自HTTP服务器的响应。

 

  

 


1  HTTP客户程序与HTTP服务器的通信过程

HTTP客户程序和HTTP服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写。用VC编写的HTTP客户程序能否与用Java编写的HTTP服务器顺利通信呢?答案是肯定的。HTTP协议严格规定了HTTP请求和HTTP响应的数据格式,只要HTTP服务器与客户程序都遵守HTTP协议,就能彼此看得懂对方发送的消息。

1HTTP请求

HTTP协议规定,HTTP请求由三部分构成:请求方法、URIHTTP协议的版本;请求头(Request Header);请求正文(Request Content)。

下面是一个HTTP请求的例子:

POST /hello.htm HTTP/1.1

Accept: image/gif, image/jpeg, */*

Referer: http://localhost/login.htm

Accept-Language: en,zh-cn;q=0.5

Content-Type: application/x-www-form-urlencoded

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)

Host: localhost

Content-Length: 43

Connection: Keep-Alive

Cache-Control: no-cache

username=weiqin&password=1234&submit=submit

1)请求方式、URIHTTP协议的版本

HTTP请求的第一行包括请求方式、URI和协议版本这三项内容,以空格分开:

POST /hello.htm HTTP/1.1

在以上代码中,“POST” 表示请求方式,“/hello.htm”表示URI,“HTTP/1.1 表示HTTP协议的版本。

根据HTTP协议,HTTP请求可以使用多种请求方式,主要包括:

1)       GET

这种请求方式最为常见,客户程序通过这种请求方式访问服务器上的一个文档,服务器把文档发送给客户程序。

2)       POST

客户程序可通过这种方式发送大量信息给服务器。在HTTP请求中除了包含要访问的文档的URI,还包括大量的请求正文,这些请求正文中通常会包含大量HTML表单数据。

3)       HEAD

客户程序和服务器之间交流一些内部数据,服务器不会返回具体的文档。当使用GETPOST方法时,服务器最后都将特定的文档返回给客户程序。而HEAD请求方式则不同,它仅仅交流一些内部数据,这些数据不会影响用户浏览网页的过程,可以说对用户是透明的。HEAD请求方式通常不单独使用,而是为其他请求方式起辅助作用。一些搜索引擎使用HEAD请求方式来获得网页的标志信息,还有一些HTTP服务器进行安全认证时,用这个方式来传递认证信息。

4)       PUT

客户程序通过这种方式把文档上传给服务器。

5)       DELETE

客户程序通过这种方式来删除远程服务器上的某个文档。客户程序可以利用PUTDELETE请求方式来管理远程服务器上的文档。

GETPOST请求方式最常用,而PUTDELETE请求方式并不常用,因而不少HTTP服务器并不支持PUTDELETE请求方式。

URIUniversal Resource Identifier,统一资源定位符)用于标识要访问的网络资源。在HTTP请求中,通常只要给出相对于服务器的根目录的相对目录即可,因此以“/”开头。

HTTP请求的第一行的最后一部分内容为客户程序使用的HTTP协议的版本。

2)请求头(Request Header

请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器的类型、所用的语言、请求正文的类型,以及请求正文的长度等:

Accept: image/gif, image/jpeg, */*

Referer: http://localhost/login.htm

Accept-Language: en,zh-cn;q=0.5   //浏览器所用的语言

Content-Type: application/x-www-form-urlencoded     //正文类型

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)   //浏览器类型

Host: localhost   //远程主机

Content-Length: 43   //正文长度

Connection: Keep-Alive

Cache-Control: no-cache

3)请求正文(Request Content

HTTP协议规定,请求头和请求正文之间必须以空行分割,这个空行非常重要,它表示请求头已经结束,接下来是请求正文。请求正文中可以包含客户以POST方式提交的表单数据:

username=weiqin&password=1234

在以上HTTP请求例子中,请求正文只有一行内容。在实际应用中,HTTP请求的正文可以包含更多的内容。

2HTTP响应

HTTP请求相似,HTTP响应也由三部分构成:HTTP协议的版本、状态代码、描述;响应头(Response Header);响应正文(Response Content)。

下面是一个HTTP响应的例子:

HTTP/1.1 200 OK

Server: nio/1.1

Content-type: text/html; charset=GBK

Content-length: 102

<html>

<head>

  <title>helloapp</title>

</head>

<body >

  <h1>hello</h1>

</body>

</html>

1HTTP协议的版本、状态代码、描述

HTTP响应的第一行包括服务器使用的HTTP协议的版本、状态代码,以及对状态代码的描述,这三项内容之间以空格分割。在本例中,使用HTTP1.1协议,状态代码为200,该状态代码表示服务器已经成功地处理了客户端发出的请求:

HTTP/1.1 200 OK

状态代码是一个3位整数,以12345开头:

l         1xx:信息提示,表示临时的响应。

l         2xx:响应成功,表明服务器成功地接收了客户端请求。

l         3xx:重定向。

l         4xx:客户端错误,表明客户端可能有问题。

l         5xx:服务器错误,表明服务器由于遇到某种错误而不能响应客户请求。

以下是一些常见的状态代码:

l         200:响应成功。

l         400:错误的请求。客户发送的HTTP请求不正确。

l         404:文件不存在。在服务器上没有客户要求访问的文档。

l         405:服务器不支持客户的请求方式。

l         500:服务器内部错误。

2)响应头(Response Header

响应头也和请求头一样包含许多有用的信息,例如服务器类型、正文类型和正文长度等:

Server: nio/1.1    //服务器类型

Content-type: text/html; charset=GBK   //正文类型

Content-length: 102  //正文长度

3)响应正文(Response Content

响应正文就是服务器返回的具体文档,最常见的是HTML网页:

<html>

<head>

  <title>helloapp</title>

</head>

<body >

  <h1>hello</h1>

</body>

</html>

HTTP请求头与请求正文之间必须用空行分割,同样,HTTP响应头与响应正文之间也必须用空行分隔。

3.测试HTTP请求

当用户在浏览器中输入一个URL,浏览器就会生成一个HTTP请求,建立与远程HTTP服务器的连结,然后把HTTP请求发送给远程HTTP服务器,HTTP服务器再返回相应的网页,浏览器最后把这个网页显示出来。当浏览器与服务器之间的数据交换完毕,就会断开连结。如果用户希望访问新的网页,浏览器必须再次建立与服务器的连结。

例程1SimpleHttpServer)创建了一个非常简单的HTTP服务器,它接收客户程序的HTTP请求,把它打印到控制台。然后对HTTP请求做简单的解析,如果客户程序请求访问login.htm,就返回该网页,否则一律返回hello.htm网页。login.htmhello.htm文件位于classpath下的root目录下。

SimpleHttpServer监听80端口,按照阻塞模式工作,采用线程池来处理每个客户请求。

//例程1  SimpleHttpServer.java(阻塞模式)

//此处省略import语句

public class SimpleHttpServer {

  private int port=80;

  private ServerSocketChannel serverSocketChannel = null;

  private ExecutorService executorService;

  private static final int POOL_MULTIPLE = 4;

 

  public SimpleHttpServer() throws IOException {

    executorService= Executors.newFixedThreadPool(

        Runtime.getRuntime().availableProcessors() * POOL_MULTIPLE);

    serverSocketChannel= ServerSocketChannel.open();

    serverSocketChannel.socket().setReuseAddress(true);

    serverSocketChannel.socket().bind(new InetSocketAddress(port));

    System.out.println("服务器启动");

  }

  public void service() {

    while (true) {

      SocketChannel socketChannel=null;

      try {

        socketChannel = serverSocketChannel.accept();

        executorService.execute(new Handler(socketChannel));

      }catch (IOException e) {

         e.printStackTrace();

      }

    }

  }

  public static void main(String args[])throws IOException {

    new SimpleHttpServer().service();

  }

  class Handler implements Runnable{   //Handler是内部类,负责处理HTTP请求

  private SocketChannel socketChannel;

  public Handler(SocketChannel socketChannel){

    this.socketChannel=socketChannel;

  }

  public void run(){

    handle(socketChannel);

  }

  public void handle(SocketChannel socketChannel){

    try {

       Socket socket=socketChannel.socket();

       System.out.println("接收到客户连接,来自: " +

      socket.getInetAddress() + ":" +socket.getPort());

 

       ByteBuffer buffer=ByteBuffer.allocate(1024);

       socketChannel.read(buffer);  //接收HTTP请求,假定其长度不超过1024个字节

       buffer.flip();

       String request=decode(buffer);

       System.out.print(request);  //打印HTTP请求

 

       //生成HTTP响应结果

       StringBuffer sb=new StringBuffer("HTTP/1.1 200 OK\r\n");

       sb.append("Content-Type:text/html\r\n\r\n");

       socketChannel.write(encode(sb.toString())); //发送HTTP响应的第一行和响应头

 

         FileInputStream in;

         //获得HTTP请求的第一行

         String firstLineOfRequest=request.substring(0,request.indexOf("\r\n"));

         if(firstLineOfRequest.indexOf("login.htm")!=-1)

            in=new FileInputStream("login.htm");

         else

            in=new FileInputStream("hello.htm");

 

        FileChannel fileChannel=in.getChannel();

        fileChannel.transferTo(0,fileChannel.size(),socketChannel);//发送响应正文

      }catch (Exception e) {

         e.printStackTrace();

      }finally {

         try{

           if(socketChannel!=null)socketChannel.close();  //关闭连结

         }catch (IOException e) {e.printStackTrace();}

      }

  }

  private Charset charset=Charset.forName("GBK");

  public String decode(ByteBuffer buffer){    } //解码

  public ByteBuffer encode(String str){   } //编码

  } //#Handler内部类

}//#SimpleHttpServer

运行“java SimpleHttpServer”命令,就启动了HTTP服务器,然后打开一个IE浏览器,按照如下步骤访问HTTP服务器。根据服务器端控制台的打印结果,可以了解IE浏览器发送给服务器的HTTP请求信息。

IE浏览器中输入URLhttp://localhost:80/login.htm或者http://localhost/login.htm。默认情况下,IE浏览器总是与远程HTTP服务器的80端口建立连接,因此在URL中可以不指定80端口。图2显示了IE浏览器接收到的网页,以及服务器接收到的HTTP请求。


2  浏览器按照GET方式访问login.htm

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089