socket编程入门,需要实现的目标:
1,不用框架利用socket编程实现一个http服务框架
2,实现基本的Request/Response封装
3, 思考io和线程模型的关系
首先我们了解一下网络插槽socket,当客户端和服务端在进行交互的时候,会分别产生一个socket,然后这两个socket进行交互,比如客户端想发送信息的时候,先发给socket,接收信息会从socket里读, 这里可以看出socket类似于文件,其实很多操作系统把socket封装成了文件。
服务端线程模型:
当我们的操作系统接收到客户端发出的web请求的时候,它会先把这个请求(比如tcp连接的请求)扔到Pending Queue里面,如果满了操作系统会拒绝请求,否则这个请求不断的积累到队列里,直到某一个线程进行accept这个系统调用,这个系统调用会从队列里拿出一个请求,接收之后会在操作系统内核中形成socket文件,这个socket文件对应着资源的FD(可以理解为操作文件资源的东西,其实就是socket),因为具体的文件在内核中,用文件的是具体的线程,这个线程不断循环,不断接收请求生成对应的FD,然后由工作线程读取socket的内容,分析其中的内容,处理完后写入socket文件,操作系统监听到文件被写入后,会把结果送到网卡,通过网络传给请求方。
第一个httpserver:
package com.hai; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * @author caohailiang * @date 2022/6/27-17:15 */ public class RawHttpServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8001); // Main Thread while (true){ // Blocking--- // Thread--->sleep Socket socket = serverSocket.accept(); // socket来操控内核中的文件 System.out.println("A socket created"); DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(dataInputStream)); StringBuilder stringBuilder = new StringBuilder(); String line=""; // Readline -> line end '\n' while (!(line=bufferedReader.readLine()).isEmpty()){ stringBuilder.append(line); } String request = stringBuilder.toString(); System.out.println(request); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bufferedWriter.write("HTTP/1.1 200 ok\n\nHello World!\n"); bufferedWriter.flush(); socket.close(); } } }
抽象成类:
public class Step1Server { ServerSocket serverSocket; Function<String,String> handler; public Step1Server(Function<String,String> handler){ this.handler=handler; } // pending Queue public void listen(int port) throws IOException { serverSocket = new ServerSocket(port); while (true){ this.accept(); } } void accept() throws IOException { // Blocking--- // Thread--->sleep try { Socket socket = serverSocket.accept(); // socket来操控内核中的文件 System.out.println("A socket created"); DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(dataInputStream)); StringBuilder stringBuilder = new StringBuilder(); String line=""; // Readline -> line end '\n' while (true){ line=bufferedReader.readLine(); if(line==null||line.isEmpty()){ break; } stringBuilder.append(line+'\n'); } String request = stringBuilder.toString(); System.out.println(request); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); String response = this.handler.apply(request); bufferedWriter.write(response); bufferedWriter.flush(); socket.close(); } catch (SocketException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { Step1Server server = new Step1Server(req -> { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "HTTP/1.1 200 ok\n\nHello World1!\n"; }); server.listen(8001); }
http服务的多线程优化:
public class Step2Server { ServerSocket serverSocket; Function<String,String> handler; public Step2Server(Function<String,String> handler){ this.handler=handler; } // pending Queue public void listen(int port) throws IOException { serverSocket = new ServerSocket(port); while (true){ this.accept(); } } void accept() throws IOException { // Blocking.. Socket socket = serverSocket.accept(); new Thread(()->{ try { this.hander(socket); } catch (Exception e) { e.printStackTrace(); } }).start(); } void hander(Socket socket) throws IOException { // Blocking--- // Thread--->sleep try { // Socket socket = serverSocket.accept(); // socket来操控内核中的文件 System.out.println("A socket created"); DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(dataInputStream)); StringBuilder stringBuilder = new StringBuilder(); String line=""; // Readline -> line end '\n' while (true){ line=bufferedReader.readLine(); if(line==null||line.isEmpty()){ break; } stringBuilder.append(line+'\n'); } String request = stringBuilder.toString(); System.out.println(request); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); String response = this.handler.apply(request); bufferedWriter.write(response); bufferedWriter.flush(); socket.close(); } catch (SocketException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { Step2Server server = new Step2Server(req -> { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "HTTP/1.1 200 ok\n\nHello World1!\n"; }); server.listen(8001); } }