手写HTTP服务器 —— Java 网络编程

2020-10-27   18 次阅读


Java 网络编程概览

前言:

手写 HTTP 服务器需要使用到 ServerSocket 来绑定一个端口,监听客户端的请求,这是 Java 网络编程提供的能力。

相关的源码在 java.net 包中,它们提供了低层次的通信细节,可以直接使用这些类和接口来专注于业务逻辑,而不用关注通信细节。

java.net 包提供了两种常见的网络协议支持:

  • TCP : TCP 是传输控制协议的缩写,保障了两个应用程序之间的可靠通信。
  • UDP: UDP 是用户数据协议的缩写,它是一个无连接的协议

Socket 与 ServerSocket

Socket 底层是 TCP 协议,提供了两台计算机之间的通信机制,客户端程序创建一个 Socket,并尝试连接服务器的 Socket。

  • java.net.Socket ---> Java 对 客户端 Socket 的实现
  • java.net.ServerSocket ---> Java 对**服务端 Socket** 的实现

客户端与服务端直接可以建立连接,然后进行通信。

下面是 Socket 通信中建立连接的基本流程:

  • 1、服务器实例化一个 ServerSocket 对象,并且绑定一个具体的端口来接收请求
  • 2、 服务器调用 ServerSocket 的 accept 方法,该方法一直等待,直到客户端连接到服务器的监听端口
  • 3、 服务器监听端口时,客户端实例化一个 Socket 对象,指定服务器的名称与端口号进行连接。
  • 4、 服务器端,accept 方法返回服务器上一个新的 Socket 引用,该引用连接到客户端的 Socket 上。

连接建立后,可以通过使用 I/O 流进行通信,每个 Socket 都有一个 输出流和一个输入流。

客户端的输出流连接到服务端的输入流,客户端的输入流连接到服务端的输出流。

TCP 是一个 双向通信协议 ,因此数据可以通过两个数据流在同一时间发送。

ServerSocket

Java 中提供服务器端套接字功能的类。通过它可以让主机监听某个端口,接收其他端的请求,并且处理完后可以对其他端做出响应。

但是真正处理请求的是 SocketImpl 类,它提供了一个创建 SocketImpl 实例的工厂,使用了工厂模式。

默认实现类是 SockesSocketImplServerSocketSocket 只是一个门面模式。

创建 ServerSocket

构造函数描述
ServerSocket()创建非绑定服务器套接字。
ServerSocket(int port)创建绑定到 特定端口 的服务器套接字。
ServerSocket(int port, int backlog)利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress address)使用指定的端口、监听 backlog 和要绑定到的本地 IP 地址创建服务器。

Socket 通信示例:

创建一个 ServerSocket,并打印 HTTP 请求的具体内容:

public class Server01 {
    public static void main(String[] args) {
        Server01 server = new Server01();
        server.start();
    }
    private ServerSocket serverSocket;
    public void start() {
        // 初始化 serversocket 实例
        try {
            serverSocket = new ServerSocket(8888);
            // 接收浏览器请求与
            receive();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("服务启动失败");
        }
    }

    public void receive() {
        try {
            Socket client = serverSocket.accept();
            System.out.println("一个客户端与服务器建立了连接");

            // 通过输入流获取请求协议
            InputStream is = client.getInputStream();

            // 一次性读取全部请求相关信息
            byte[] data = new byte[1024 * 1024];
            int len = is.read(data);
            String requestInfo = new String(data, 0, len);

            System.out.println("请求内容:"+requestInfo);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("客户端建立连接错误");
        }
    }

    public void stop() {

    }
}

这里使用浏览器访问接口 8888,可以看到虽然没有返回给客户端数据,但是我的服务端已经接收到了 客户端的 Socket 请求,并打印了对应的数据。

image-20201027200821763

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

最是人间留不住,曾是惊鸿照影来。