• 计算机网络(第四弹) --- TCP 套接字编程的通信模型及实现流程


    1 流套接字通信流程

    在这里插入图片描述

    2 TCP 流套接字编程

    这里我们简单写一个回显服务器当做 🌰 来解读流套接字编程用到的方法和逻辑, ServletSocket 是创建 TCP 服务端 Socket 的 API, 主要处理客户端的连接, Socket 主要用来和客户端进行具体的交互, 这里还需要注意 TCP 协议有连接, 类似于打电话; 此处我们实现的是一个长连接版本的服务器, 关于长连接和短连接的区别, 后面会解释.
    服务端代码如下:

    public class TcpEchoServer {
        private ServerSocket serverSocket = null;
        public TcpEchoServer(int port) throws IOException {
            serverSocket = new ServerSocket(port);
        }
    
        public void star() throws IOException {
            System.out.println("服务器启动啦!");
            while (true) {
                Socket clientSocket = serverSocket.accept();
                processConnection(clientSocket);
            }
        }
    
        private void processConnection(Socket clientSocket) {
            System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),
                    clientSocket.getPort());
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) {
                while (true) {
                    String request = bufferedReader.readLine();
                    bufferedWriter.write(reponse + "\n");
                    bufferedWriter.flush();
                    System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort(), request,reponse);
                }
            } catch (IOException e) {
                System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort());
            }
        }
    
        private String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoServer server = new TcpEchoServer(9090);
            server.star();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    代码流程解读:
    在这里插入图片描述
    客户端代码如下:

    public class TcpEchoClient {
        private Socket socket = null;
        public TcpEchoClient (String serverIp, int serverPort) throws IOException {
            socket = new Socket(serverIp,serverPort);
        }
        
        public void start() {
            System.out.println("客户端启动了!");
            Scanner scanner = new Scanner(System.in);
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))){
                while (true) {
                    System.out.println("--> ");
                    String request = scanner.nextLine();
                    if("exit".equals(request)) {
                        break;
                    }
                    bufferedWriter.write(request + "\n");
                    bufferedWriter.flush();
                    String response = bufferedReader.readLine();
                    System.out.println(response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
            client.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    代码流程解读:
    在这里插入图片描述
    运行结果:
    在这里插入图片描述

    3 注意事项

    • TCP 的连接管理是由操作系统内核来管理的, 整体思路就是先描述, 再组织; 描述的主要是通信中的五元组 / 协议类型 / 源 IP 端口 / 目的 IP 端口等, 组织主要是使用一个阻塞队列来组织若干个连接对象;
    • 客户端和服务器建立连接的过程, 完全由内核来进行负责, 应用程序的代码是感知不到的, 当建立成功之后, 内核已经把这个连接对象放到了阻塞队列中了;
    • 代码中的 accept 就是从阻塞队列中取出一个连接对象, 在应用程序中的化身就是 Socket 对象;
    • 如果服务器启动之后么, 没有客户端建立连接, 此时代码中调用 accept 就会阻塞, 阻塞到真正有客户端建立连接为止;
    • 其实这就是一个生产者消费者模型, 后续针对数据的读写都是针对 clientSocket 这个对象进行展开的.

    4 关于长短连接的理解

    4.1 概念

    • 短连接: 一个连接中, 客户端和服务器之间只交互一次, 交互完毕就断开连接; 每次接收到数据并返回响应后, 都关闭连接;
    • 长连接: 一个连接中, 客户端和服务器之间交互 n 次, 直到满足一定的条件才断开, 因此长连接的效率会更高, 会避免反复建立连接和断开连接的过程; 总之, 不关闭连接, 一直保持连接状态, 双方不停的收发数据.

    4.2 区别

    • 建立连接 / 关闭连接的耗时方面: 短连接每次请求 / 响应都需要建立连接 / 关闭连接; 而长连接只需要第一次建立连接, 之后的请求 / 响应都可以直接传输, 相对来说建立连接/ 关闭连接也是耗时的, 长连接的效率更高;
    • 主动发送请求方面: 短连接一般是客户端主动向服务器端发送请求; 而长连接可以是客户端主动发送请求, 也可以是服务端主动发;
    • 使用场景方面: 短连接适用于客户端请求频率不高的场景, 如浏览网页等; 而长连接适用于客户端与服务器端通信频率频繁的场景, 微信聊天就是长连接的场景.
  • 相关阅读:
    Linux- 命名信号量和无名信号量的区别
    ps丢失d3dcompiler_47.dll怎么办,这四个方法都能解决
    机器人抓取(七、八)—— kinect 相机的 ros 服务器及 基于gqcnn 抓取 ros 服务器
    贪吃蛇和俄罗斯方块
    浅谈Linux的五大便捷之处
    【轻松学习】教你十分钟学会Python函数式编程
    接口访问量统计
    【软件测试】理论知识基础第一章
    Q-Vision+CANpro Max总线解决方案
    linux php-fpm进程 cpu占用过高 解决方法
  • 原文地址:https://blog.csdn.net/Onion_521257/article/details/127812462