自己动手,写个Web服务器(Java版)——第一篇、浏览器和Web服务器的语言Http

来源:互联网 发布:网站如何seo 编辑:程序博客网 时间:2024/05/22 17:49

浏览器如何根Web服务器交谈呢?请看下图

http协议图

 

浏览器和Web服务器通过一个叫TCP的管子通话。浏览器先说:我要……。Web服务器回答:给你……。如此往复。Web服务器不会主动回答的,只有浏览器发出请求,Web服务器才会回答。

浏览器和Web服务器都是机器,不懂人的语言。它们有自己的语言——http。其中请求和响应的语法都差不多。都是由头、(空行)、体组合而成。头是由一行一行的格式如:name value(回车换行)的行组成的。

下面就来写两个简单的程序看看浏览器和Web服务器到底说了些什么吧:)

1       浏览器的请求

Server是用Java写第一版的Web服务器。它打开一个80端口的ServerSocket等待浏览器连入。当浏览器接入后,把浏览器发送的请求打印出来。

package mywebserver1;

 

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.StringTokenizer;

 

/**

 * @author allen

 * @version 0.1

 */

public class Server {

   public static void main(String[] args) {

 

            try {

                     ServerSocket ss = new ServerSocket(80);

                     Socket s = ss.accept();

                     BufferedReader br = new BufferedReader(new InputStreamReader(s

                                        .getInputStream()));

                     String requestLine = "";

                    

                     char[] body;

                     boolean haveBody = false;

                     int length = 0;

                     do {

                               requestLine = br.readLine();

                               System.out.println(requestLine);

                               // post方式提交的请求还要取得,请求体的内容

                               if (requestLine.startsWith("Content-Length")) {

                                        StringTokenizer st = new StringTokenizer(requestLine, ":");

                                        st.nextToken();

                                        String bodyLength = st.nextToken().trim();

                                        length = Integer.parseInt(bodyLength);

                                        haveBody = true;

                               }

                               if (requestLine.equals("") && haveBody) {

                                        body = new char[length];

                                        br.read(body, 0, length);

                                        System.out.println(new String(body));

                               }

                     } while (!requestLine.equals(""));

                    

                     br.close();

                     s.close();

                     ss.close();

            } catch (IOException e) {

                     e.printStackTrace();

            } finally {

                     //资源清理代码

            }

   }

}

以下是控制台打印出的浏览器请求的内容

1.       Get方式

 

GET / HTTP/1.1

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, */*

Accept-Language: zh-cn

Accept-Encoding: gzip, deflate

If-Modified-Since: Tue, 02 Sep 2008 06:15:50 GMT

If-None-Match: W/"648-1220336150812"

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; aff-kingsoft-ciba)

Host: 127.0.0.1

Connection: Keep-Alive

Cookie: __utma=96992031.200084460.1247665275.1247665275.1247665275.1; __utmz=96992031.1247665275.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)

 

2.       Post方式

 

POST / HTTP/1.1

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, */*

Accept-Language: zh-cn

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

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; aff-kingsoft-ciba)

Host: 127.0.0.1

Content-Length: 19

Connection: Keep-Alive

Cache-Control: no-cache

Cookie: __utma=96992031.200084460.1247665275.1247665275.1247665275.1; __utmz=96992031.1247665275.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)

 

user=Mike&pass=1234

这个是测试用的index.html页面

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

   <HEAD>

            <META http-equiv=Content-Type content="text/html; charset=gb2312">

   </HEAD>

   <BODY>

            <a href="http://127.0.0.1/">Get方式提交的请求!</a>

            <form name="FormRequest" action="http://127.0.0.1/" method="post">

                     <input name="user" type="text" value="Mike" />

                     <input name="pass" type="password" value="1234" />

                     <input type="submit" value="Test"/>

            </form>

   </BODY>

</HTML>

2       Web服务器的回应

Client.java是测试用的客户端。它手工构造了一个请求(访问的是www.baidu.com),然后把服务器的响应打出来。

/**

 *

 */

package mywebserver1;

 

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.net.InetAddress;

import java.net.Socket;

import java.net.UnknownHostException;

import java.nio.charset.Charset;

import java.util.StringTokenizer;

 

/**

 * @author allen

 * @version 0.1

 */

public class Client {

 

   /**

    * @param args

    */

   public static void main(String[] args) {

 

            String host = "www.baidu.com";

            String resource = "/";

            int port = 80;

            //手工构造请求

            StringBuilder request = new StringBuilder();

            request.append("GET " + resource + " HTTP/1.1/n");

            request.append("/n");

            System.out.println("请求是:/n" + request.toString());

 

            try {

                     InetAddress[] ipaddr = InetAddress.getAllByName(host);

                     Socket s = new Socket(ipaddr[0], port);

                     BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s

                                        .getOutputStream()));

                     bw.write(request.toString());

                     bw.flush();

                     // 发送完请求,开始接收响应

                     BufferedReader br = new BufferedReader(new InputStreamReader(s

                                        .getInputStream()));

                     String requestLine = "";                       

                     char[] body;

                     boolean haveBody = false;

                     int length = 0;

                     boolean ifchunked = false;

                     do {

                               requestLine = br.readLine();

                               System.out.println(requestLine);

                               // 响应体的取得,与请求体的取得类似。

                               if (requestLine.startsWith("Content-Length")) {

                                        StringTokenizer st = new StringTokenizer(requestLine, ":");

                                        st.nextToken();

                                        String bodyLength = st.nextToken().trim();

                                        length = Integer.parseInt(bodyLength);

                                        haveBody = true;

                               }

                               if (requestLine.startsWith("Transfer-Encoding")) {

                                        StringTokenizer st = new StringTokenizer(requestLine, ":");

                                        st.nextToken();

                                        String transferEncoding = st.nextToken().trim();

                                        if ("chunked".equalsIgnoreCase(transferEncoding)) {

                                                 ifchunked = true;

                                        }

                               }

                               //读取响应Body

                               if (requestLine.equals("") && haveBody) {

                                        body = new char[length];

                                        br.read(body, 0, length);

                                        String responseBody = new String(body);

                                        System.out.println(responseBody.length());

                                        System.out.println(responseBody);

                               }

                               //响应的Transfer-Encodingchunked,需要特殊的处理

                               if (requestLine.equals("") && ifchunked) {

                                        char[] chunkedbody = new char[1024];

                                        int readin = 0;

                                        do {

                                                 readin = br.read(chunkedbody);

                                                 System.out.println(new String(chunkedbody, 0, readin));

                                        } while (readin > 0);

                               }

                     } while (!requestLine.equals(""));

 

                     bw.close();

                     br.close();

                     s.close();

            } catch (UnknownHostException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

            } catch (IOException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

            }

   }

}

以下是百度返回的响应

 

HTTP/1.1 200 OK

Date: Sun, 26 Jul 2009 13:34:30 GMT

Server: BWS/1.0

Content-Length: 3509

Content-Type: text/html

Cache-Control: private

Expires: Sun, 26 Jul 2009 13:34:30 GMT

Set-Cookie: BAIDUID=1FAAFC4F63BD53DED54944E06B8FACEA:FG=1; expires=Sun, 26-Jul-39 13:34:30 GMT; path=/; domain=.baidu.com

P3P: CP=" OTI DSP COR IVA OUR IND COM "

 

3509

<html><head><meta http-equiv=Content-Type content="text/html;charset=gb2312"><title>百度一下,你就知道</title>省略了</html><!--c8e53c7e25ef8339-->_

原创粉丝点击