Web服务器开发小试牛刀——使用Socket通信

在web开发的过程中,大家都会接触到服务器这个词。

我最近在学Java Web的时候就用到了tomcat这个服务器,我就在想如何可以在自己编写一个服务器。
我们做开发的时候都知道,一个请求一般都会包括请求头、请求体,响应一般也会包括响应头和响应内容。
下面我们来写一下HttpServer服务器这个类:

package test;

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

public class HttpServer {
    public static void main(String[] args) {
        int port;
        ServerSocket serverSocket;
        System.out.println("port = 8080(默认)");
        port = 8080;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器正在监听端口:"+serverSocket.getLocalPort());
            while(true){
                final Socket socket = serverSocket.accept();
                System.out.println("建立了一个与客户的一个新的TCP链接,客户地址为:"+socket.getInetAddress()+":"+socket.getPort());
                service(socket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void service(Socket socket) throws Exception {
        InputStream socketInputStream = socket.getInputStream();
        Thread.sleep(500);
        int size = socketInputStream.available();
        byte[] buffer = new byte[size];
        socketInputStream.read(buffer);
        String request = new String(buffer);
        System.out.println(request);    //打印HTTP请求数据

        //解析HTTP请求
        //获取HTTP请求的第一行
        int endIndex = request.indexOf("\r\n");
        if (endIndex == -1){
            endIndex = request.length();
        }
        String firstLineOfRequest = request.substring(0,endIndex);

        //解析HTTP请求第一行
        String[] parts = firstLineOfRequest.split(" ");
        String uri = "";
        if (parts.length >= 2){
            uri = parts[1];
        }
        String contentType;
        if (uri.indexOf("html")!=-1 || uri.indexOf("htm")!=-1){
            contentType = "text/html";
        }
        else if(uri.indexOf("jpg")!=-1 || uri.indexOf("jpeg")!= -1){
            contentType = "image/jpeg";
        }
        else if (uri.indexOf("gif")!=-1){
            contentType = "image/gif";
        }
        else {
            contentType = "application/octet-stream";   //字节流类型
        }

        //创建HTTP响应结果
        //HTTP响应第一行
        String responseFirstLine = "HTTP/1.1 200 OK\r\n";
        String responseHeader = "Content-Type:"+contentType+"\r\n\r\n";
        InputStream inputStream = HttpServer.class.getResourceAsStream("root/" + uri);

        //发送HTTP响应结果
        OutputStream socketOutputStream = socket.getOutputStream();
        socketOutputStream.write(responseFirstLine.getBytes());
        socketOutputStream.write(responseHeader.getBytes());

        int len = 0;
        buffer = new byte[128];
        while ((len = inputStream.read(buffer))!=-1){
            socketOutputStream.write(buffer,0,len);
        }
        Thread.sleep(100);
        socket.close();
    }
}

整个服务器分为:

  1. 监听8080端口,建立TCP连接
  2. 处理请求头,截取请求的第一行的后缀名
  3. 由于提前设定好要访问的资源位于同级目录下的root文件夹下,所以采用HttpServer.class.getResourceAsStream("root/" + uri)获取输入流
  4. 设定200成功响应头,将响应的第一行、响应文件的类型以及响应主题发送回客户端
  5. 关闭连接

下面我们再写客户端的代码:

package cn.yuanzhen25800.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class HttpClient {
    public static void main(String[] args) {
        //确定请求的uri
        String uri = "hello.html";
        if (args.length != 0){
            uri = args[0];
        }
        doGet("localhost",8080,uri);
    }

    public static void doGet(String host, int port, String uri) {
        Socket socket = null;
        try {
            socket = new Socket(host,port);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            StringBuffer stringBuffer = new StringBuffer("GET "+uri+" HTTP/1.1\r\n");
            //HTTP请求头
            stringBuffer.append("Accept: */*\r\n");
            stringBuffer.append("Accept-Language: zh-cn\r\n");
            stringBuffer.append("Accept-Encoding: gzip,deflate\r\n");
            stringBuffer.append("User-Agent: HTTPClient\r\n");
            stringBuffer.append("Host: localhost:8080\r\n");
            stringBuffer.append("Connection: Keep-Alive\r\n");

            //发送HTTP请求
            OutputStream socketOutputStream = socket.getOutputStream();
            socketOutputStream.write(stringBuffer.toString().getBytes());
            Thread.sleep(2000);

            //接收响应结果
            InputStream socketInputStream = socket.getInputStream();
            int size = socketInputStream.available();
            byte[] buffer = new byte[size];
            socketInputStream.read(buffer);
            System.out.println(new String(buffer));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

客户端请求同服务器功能差不多,
分以下几个步骤:

  1. 建立一个TCP连接,确定服务器地址、端口以及请求的资源路径
  2. 编写请求头
  3. 发送请求头信息
  4. 接收响应结果并打印

下面是运行的结果图:


当然,服务器开启后在浏览器端也是可以请求的,下面是结果图:

以上是我的一些个人见解,如有错误欢迎大家斧正!

posted @ 2022-01-10 16:33  自觉_自律_自由  阅读(294)  评论(0)    收藏  举报