TCP网络编程中connect、listen、accept三者之间的关系
这篇文章围绕 TCP 服务端与客户端连接建立流程,重点澄清了 connect、listen、accept 与内核连接队列之间的真实关系。connect 与 listen 的职责边界connect() 与 listen() 本身不直接完成三次握手,它们主要是向内核发出“连接请求/监听意图”。TCP 三次握手由内核协议栈执…
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: {{< image src="https://s2.loli.net/2022/01/26/Ric1E3kurq5f6WI.png" caption="整个函数调用(image)" src_s="https://s2.loli.net/2022/01/26/Ric1E3kurq5f6WI.png" src_l="https://s2.loli.net/2022/01/26/Ric1E3kurq5f6WI.png" >}}accept()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手,而这个连接的过程是由内核完成,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 Linux 内核自动完成 TCP 三次握手连接,最后把连接的结果返回给这个函数的返回值(成功连接为0, 失败为-1)。 通常的情况,客户端的 connect() 函数默认会一直阻塞,直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。listen()函数 对于服务器,它是被动连接的。举一个生活中的例子,通常的情况下,移动的客服(相当于服务器)是等待着客户(相当于客户端)电话的到来。而这个过程,需要调用listen()函数。#include<sys/socket.h> int listen(int sockfd, int backlog); listen() 函数的主要作用就是将套接字( sockfd )变成被动的连接监听套接字(被动等待客户端的连接),至于参数 backlog 的作用是设置内核中连接队列的长度(这个长度有什么用,后面做详细的解释),TCP 三次握手也不是由这个函数完成,listen()的作用仅仅告诉内核一些信息。 这样的话,当有一个客户端主动连接 connect(),Linux 内核就自动完成TCP 三次握手,将建立好的链接自动存储到队列中,如此重复。 所以,只要 TCP 服务器调用了 listen(),客户端就可以通过 connect() 和服务器建立连接,而这个连接的过程是由内核完成。 {{< image src="https://s2.loli.net/2022/01/26/mBSOezpYvJFqkVb.png" caption="三次握手" src_s="https://s2.loli.net/2022/01/26/mBSOezpYvJFqkVb.png" src_l="https://s2.loli.net/2022/01/26/mBSOezpYvJFqkVb.png" >}} 下面为测试的服务器和客户端代码,运行程序时,要先运行服务器,再运行客户端: 服务器:#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { unsigned short port = 8000; int sockfd; sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字 if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in my_addr; bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr)); if( err_log != 0) { perror("binding"); close(sockfd); exit(-1); } err_log = listen(sockfd, 10); if(err_log != 0) { perror("listen"); close(sockfd); exit(-1); } printf("listen client @port=%d...\n",port); sleep(10); // 延时10s system("netstat -an | grep 8…
正在初始化 WebAssembly 引擎…
首次编译原生模块可能需要数秒
就绪后,页面交互将以接近原生的速度运行