服务端不 listen 可以创建 tcp 连接吗

这个问题有三类答案。

上来就撸 linux kernel 源码,折腾半天,哦,终于在 tcp_rcv_state_process 里找到了 tcp_rcv_synsent_state_process 调用,后者包含:

if (th->syn) {
        /* We see SYN without ACK. It is attempt of
         * simultaneous connect with crossed SYNs.
         * Particularly, it can be connect to self.
         */
        tcp_set_state(sk, TCP_SYN_RECV);

注释也不看,看到 tcp_set_state 就很兴奋,认为自己找到了答案,斩钉截铁回答 “不 listen 也能建立起连接”。

这种回答说明此人能看懂 linux kernel c 代码,可能根本不懂 tcp,就像一个毫无乐感且不识谱的人照着一个按排好的钢琴按键序列依次按下,就能弹出正道的光的一样。

大部分人都精通 linux kernel 源码,所以任何问题都可以落实到一个查找代码的问题。

第二类答案是告诉你可以在服务端创建个 tun 设备将 syn 引到用户态程序,然后构造一个 synack 回注到 tun 设备,或者 dpdk,iptables nf_queue,自定义 nf_hook 回注 synack 也可以做同样的事吧啦吧啦… 这说明这个人懂 tcp 握手套路但显然对协议理解并不深入,他只是对 linux 网络协议栈的玩法比较擅长。

第三类答案才是正确答案,就一句话,tcp 同时打开。撸代码的那位,tcp_set_state 上面注释写得很清楚了,rfc793 3.4 小节:

The procedure also works if two TCP simultaneously initiate the procedure. When simultaneous attempt occurs, each TCP receives a “SYN” segment which carries no acknowledgment after it has sent a “SYN”.
在这里插入图片描述

[root@localhost ~]# nc -p 2234 127.0.0.1 2234

知道姿势了吗?

浙江温州皮鞋湿,下雨进水不会胖。

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP 协议中,服务端通过 `socket()` 函数创建一个 socket,并使用 `bind()` 函数将其绑定到一个特定的 IP 地址和端口号上。然后服务端通过 `listen()` 函数开始监听客户端的连接请求。 当客户端想要连接服务端时,客户端通过 `socket()` 函数创建一个 socket,并使用 `connect()` 函数指定服务端的 IP 地址和端口号。连接建立后,客户端和服务端之间就可以进行数据传输了。 在服务端接受客户端的连接时,服务端使用 `accept()` 函数等待客户端的连接请求,并返回一个新的 socket,该 socket 用于与客户端进行通信。服务端可以通过这个新的 socket 接收客户端发送的数据,并发送数据给客户端。 以下是服务端接受客户端连接的基本步骤: 1. 创建服务端 socket 2. 绑定服务端 socket 到一个 IP 地址和端口号上 3. 开始监听客户端连接请求 4. 使用 `accept()` 函数等待客户端的连接请求,并返回一个新的 socket 5. 使用新的 socket 接收客户端发送的数据,并发送数据给客户端 具体实现可以参考下面的代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int server_fd, client_fd, max_clients = MAX_CLIENTS; int opt = 1; int addrlen, activity, i, valread, sd; int max_sd; char buffer[BUFFER_SIZE]; struct sockaddr_in address; // 创建服务端 socket if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置 socket 选项,允许地址重用 if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 设置服务端 socket 的地址和端口号 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(8080); // 绑定服务端 socket 到指定的地址和端口号 if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 开始监听客户端连接请求 if(listen(server_fd, max_clients) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } addrlen = sizeof(address); puts("Waiting for connections..."); while(1) { fd_set readfds; // 清空文件描述符集合 FD_ZERO(&readfds); // 将服务端 socket 加入文件描述符集合 FD_SET(server_fd, &readfds); max_sd = server_fd; // 将连接服务端的客户端 socket 加入文件描述符集合 for(i = 0; i < max_clients; i++) { sd = client_fds[i]; if(sd > 0) { FD_SET(sd, &readfds); } if(sd > max_sd) { max_sd = sd; } } // 等待文件描述符集合中的任意一个 socket 有数据可读 activity = select(max_sd + 1, &readfds, NULL, NULL, NULL); if((activity < 0) && (errno != EINTR)) { perror("select failed"); } // 如果服务端 socket 有数据可读,表示有新的客户端连接 if(FD_ISSET(server_fd, &readfds)) { if((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } printf("New connection from %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // 将新的客户端 socket 加入到客户端 socket 数组中 for(i = 0; i < max_clients; i++) { if(client_fds[i] == 0) { client_fds[i] = client_fd; break; } } } // 如果连接服务端的某个客户端 socket 有数据可读 for(i = 0; i < max_clients; i++) { sd = client_fds[i]; if(FD_ISSET(sd, &readfds)) { if((valread = read(sd, buffer, BUFFER_SIZE)) == 0) { // 如果客户端关闭了连接,从客户端 socket 数组中移除该 socket printf("Client %d disconnected\n", sd); close(sd); client_fds[i] = 0; } else { // 将客户端发送的数据原样发送回去 buffer[valread] = '\0'; send(sd, buffer, strlen(buffer), 0); } } } } return 0; } ``` 上述代码实现了一个简单的 TCP 服务端,它可以接受多个客户端的连接,并将客户端发送的数据原样发送回去。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值