欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个简单的Web服务器(Web服务器如何工作)

程序员文章站 2022-05-08 22:51:44
...

一个简单的Web服务器(Web服务器如何工作)

分类: JavaWeb 2013-10-20 17:13 34人阅读 评论(0) 收藏 举报

 

HTTP允许Web服务器和浏览器通过Internet发送并接收数据,是一种基于“请求——响应”的协议。HTTP使用的是可靠的TCP连接,TCP协议默认使用TCP 80端口。在HTTP中,总是由客户端通过建立连接并发送HTTP请求来初始化一个事务的。Web服务器端并不负责联系客户端或建立一个到客户端的回调连接。HTTP协议是无状态的协议。 套接字是网络连接的端点。套接字使应用程序可以从网络中读取数据,可以向网络中写入数据。不同计算机上的两个应用程序可以通过连接发送或接收字节流,一次达到相互通信的目的。要创建一个套接字,需要指明其主机名和端口号,如下:

 

 

  1. new Socket("www.baidu.com", 80);
new Socket("www.baidu.com", 80);
一旦创建了Socket的实例,就可以使用该实例发送或接收字节流。要发送字节流,需要调用Socket类的getOutputStream()方法获取一个java.io.OutputStream对象。要发送文本到远程应用程序,通常需要使用返回的OutputStream对象创建一个java.io.PrintWriter对象。若想要从连接的另一端接收字节流,需要调用Socket类的getInputStream()方法,该方法会返回一个java.io.InputStream对象。

 

如果要想实现一个服务器应用程序,需要使用服务器套接字的实现(java.net.ServerSocket)。ServerSocket类和Socket类不同,服务器套接字要等待来自客户端的连接请求,当服务器套接字收到了连接请求以后,他会创建一个Socket实例来处理与客户端的通信。要创建一个服务器套接字,需要指定其主机名和监听的端口号。服务器套接字还有一个重要属性是backlog,表示在服务器在拒绝接受传入的请求之前,传入的连接请求的最大队列长度。ServerSocket类的实例化方式之一如下:

 

  1. new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
创建了ServletSocket实例后,可以使其等待传入的连接请求,该链接请求会通过服务器套接字侦听的端口上绑定地址传入。这些工作可以通过调用ServerSocket类的accept方法完成。只有当接收到连接请求时,该方法才会返回,返回值是一个Socket实例。

 

 

Web服务器应用程序实例如下:

1.Request类:

 

  1. package com.demo.tomcat;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. publicclass Request {
  5. /**
  6. * 从Socket中读取数据的缓冲区大小
  7. */
  8. privatestaticfinalint SOCKET_BUFFER_SIZE = 1024;
  9. /**
  10. * 输入流(通过与客户端通信Socket中获取)
  11. */
  12. private InputStream input;
  13. /**
  14. * Http请求的URI
  15. */
  16. private String uri;
  17. /**
  18. * Constructor
  19. * @param input 输入流
  20. */
  21. public Request(InputStream input){
  22. this.input = input;
  23. }
  24. /**
  25. * 解析Http请求的原始数据(例如,GET请求index.html的请求行为:"GET /index.html HTTP/1.1)
  26. */
  27. publicvoid parse(){
  28. //读取Socket中的数据
  29. StringBuffer sb = new StringBuffer(SOCKET_BUFFER_SIZE);
  30. int i;
  31. byte[] buf = newbyte[SOCKET_BUFFER_SIZE];
  32. try {
  33. i = input.read(buf);
  34. for(int j = 0; j < i; j++){
  35. sb.append((char) buf[j]);
  36. }
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. //解析URI
  41. if(sb.length() > 0){
  42. uri = parseURI(sb.toString());
  43. }
  44. }
  45. /**
  46. * 获取请求的URI
  47. * @param requestStr
  48. * @return
  49. */
  50. private String parseURI(String requestStr){
  51. if(requestStr == null || requestStr.length() <= 0){
  52. returnnull;
  53. }
  54. int index1,index2;
  55. index1 = requestStr.indexOf(' ');
  56. if(index1 != -1){
  57. index2 = requestStr.indexOf(' ', index1 + 1);
  58. if(index2 > index1){
  59. //截取字符串并返回
  60. return requestStr.substring(index1 + 1, index2);
  61. }
  62. }
  63. returnnull;
  64. }
  65. public String getUri() {
  66. return uri;
  67. }
  68. }
package com.demo.tomcat;

import java.io.IOException;
import java.io.InputStream;

public class Request {
    /**
     * 从Socket中读取数据的缓冲区大小
     */
    private static final int SOCKET_BUFFER_SIZE = 1024;
    /**
     * 输入流(通过与客户端通信Socket中获取)
     */
    private InputStream input;
    /**
     * Http请求的URI
     */
    private String uri;

    /**
     * Constructor
     * @param input 输入流
     */
    public Request(InputStream input){
        this.input = input;
    }
    /**
     * 解析Http请求的原始数据(例如,GET请求index.html的请求行为:"GET /index.html HTTP/1.1)
     */
    public void parse(){
        //读取Socket中的数据
        StringBuffer sb = new StringBuffer(SOCKET_BUFFER_SIZE);
        int i;
        byte[] buf = new byte[SOCKET_BUFFER_SIZE];
        try {
            i = input.read(buf);
            for(int j = 0; j < i; j++){
                sb.append((char) buf[j]);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //解析URI
        if(sb.length() > 0){
            uri = parseURI(sb.toString());
        }
    }

    /**
     * 获取请求的URI
     * @param requestStr
     * @return
     */
    private String parseURI(String requestStr){
        if(requestStr == null || requestStr.length() <= 0){
            return null;
        }
        int index1,index2;
        index1 = requestStr.indexOf(' ');
        if(index1 != -1){
            index2 = requestStr.indexOf(' ', index1 + 1);
            if(index2 > index1){
                //截取字符串并返回
                return requestStr.substring(index1 + 1, index2);
            }
        }
        return null;
    }

    public String getUri() {
        return uri;
    }
}

 

2.Response类:

 

  1. package com.demo.tomcat;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.OutputStream;
  6. publicclass Response {
  7. /**
  8. * 读取数据缓冲区大小
  9. */
  10. privatestaticfinalint BUFFER_SIZE = 1024;
  11. /**
  12. * 请求
  13. */
  14. private Request request;
  15. /**
  16. * 输出流
  17. */
  18. private OutputStream output;
  19. /**
  20. * Constructor
  21. * @param output 输出流
  22. */
  23. public Response(OutputStream output){
  24. this.output = output;
  25. }
  26. publicvoid setRequest(Request request){
  27. this.request = request;
  28. }
  29. /**
  30. * 发送一个静态资源到浏览器
  31. * @throws Exception
  32. */
  33. publicvoid sendStaticResource() throws Exception {
  34. //读取静态资源
  35. byte[] bytes = newbyte[BUFFER_SIZE];
  36. FileInputStream fis = null;
  37. try {
  38. File file = new File(HttpServer.WEB_ROOT, request.getUri());
  39. if(file.exists()){//文件存在
  40. fis = new FileInputStream(file);
  41. //读取静态资源到缓冲区
  42. int ch = fis.read(bytes, 0, BUFFER_SIZE);
  43. while( ch != -1){
  44. output.write(bytes, 0, ch);
  45. ch = fis.read(bytes, 0, BUFFER_SIZE);
  46. }
  47. } else {
  48. String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
  49. "Content-Type: text/html\r\n" +
  50. "Content-Length: 23\r\n" +
  51. "\r\n" +
  52. "<h1>File Not Found</h1>";
  53. //发送错误信息
  54. output.write(errorMessage.getBytes());
  55. }
  56. } catch (IOException e) {
  57. e.printStackTrace();
  58. } finally {
  59. fis.close();
  60. }
  61. }
  62. }
package com.demo.tomcat;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;


public class Response {
    /**
     * 读取数据缓冲区大小
     */
    private static final int BUFFER_SIZE = 1024;
    /**
     * 请求
     */
    private Request request;
    /**
     * 输出流
     */
    private OutputStream output;

    /**
     * Constructor
     * @param output 输出流
     */
    public Response(OutputStream output){
        this.output = output;
    }

    public void setRequest(Request request){
        this.request = request;
    }

    /**
     * 发送一个静态资源到浏览器
     * @throws Exception
     */
    public void sendStaticResource() throws Exception {
        //读取静态资源
        byte[] bytes = new byte[BUFFER_SIZE];
        FileInputStream fis = null;
        try {
            File file = new File(HttpServer.WEB_ROOT, request.getUri());
            if(file.exists()){//文件存在
                fis = new FileInputStream(file);
                //读取静态资源到缓冲区
                int ch = fis.read(bytes, 0, BUFFER_SIZE);
                while( ch != -1){
                    output.write(bytes, 0, ch);
                    ch = fis.read(bytes, 0, BUFFER_SIZE);
                }
            } else {
                String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
                        "Content-Type: text/html\r\n" +
                        "Content-Length: 23\r\n" +
                        "\r\n" +
                        "<h1>File Not Found</h1>";
                //发送错误信息
                output.write(errorMessage.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            fis.close();
        }

    }
}

 

3.HttpServer类:

 

  1. package com.demo.tomcat;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.OutputStream;
  6. import java.net.InetAddress;
  7. import java.net.ServerSocket;
  8. import java.net.Socket;
  9. publicclass HttpServer {
  10. /**
  11. * 访问的根路径
  12. */
  13. publicstaticfinal String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
  14. /**
  15. * 关闭服务请求命令
  16. */
  17. publicstaticfinal String SHUTDOWN_COMMAND = "/SHUTDOWN";
  18. /**
  19. * 是否关闭服务
  20. */
  21. private Boolean shutdown = false;
  22. publicstaticvoid main(String[] args){
  23. HttpServer httpServer = new HttpServer();
  24. httpServer.await();
  25. }
  26. /**
  27. * 从accept方法返回的Socket中获取InputStream对象和OutputStream对象
  28. */
  29. publicvoid await(){
  30. //创建ServerSocket对象
  31. ServerSocket serverSocket = null;
  32. //监听端口
  33. int port = 8080;
  34. try {
  35. //实例化ServerSocket对象
  36. serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. System.exit(1);
  40. }
  41. while(!shutdown){
  42. Socket socket = null;
  43. InputStream input = null;
  44. OutputStream output = null;
  45. try {
  46. socket = serverSocket.accept();
  47. input = socket.getInputStream();
  48. output = socket.getOutputStream();
  49. //创建并解析request
  50. Request request = new Request(input);
  51. request.parse();
  52. //创建response,发送静态资源给浏览器
  53. Response response = new Response(output);
  54. response.setRequest(request);
  55. response.sendStaticResource();
  56. //关闭socket
  57. socket.close();
  58. //是否关闭服务
  59. shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
  60. } catch (Exception e) {
  61. e.printStackTrace();
  62. }
  63. }
  64. }
  65. }
package com.demo.tomcat;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


public class HttpServer {
    /**
     * 访问的根路径
     */
    public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
    /**
     * 关闭服务请求命令
     */
    public static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
    /**
     * 是否关闭服务
     */
    private Boolean shutdown = false;

    public static void main(String[] args){
        HttpServer httpServer = new HttpServer();
        httpServer.await();
    }

    /**
     * 从accept方法返回的Socket中获取InputStream对象和OutputStream对象
     */
    public void await(){
        //创建ServerSocket对象
        ServerSocket serverSocket = null;
        //监听端口
        int port = 8080;
        try {
            //实例化ServerSocket对象
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        while(!shutdown){
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;

            try {
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();
                //创建并解析request
                Request request = new Request(input);
                request.parse();
                //创建response,发送静态资源给浏览器
                Response response = new Response(output);
                response.setRequest(request);
                response.sendStaticResource();
                //关闭socket
                socket.close();
                //是否关闭服务
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 

注意:需要在项目所在的路径下添加一个名为"webroot"的文件夹,该文件夹下放置你要访问你的文件(如test.txt),输入相应的URI(如http://lcoalhost:8080/test.txt),即可访问到该资源文件。

参考:深入剖析Tomcat第一章