You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何未调用accept()时telnet仍能与监听Socket建立连接?

问题:未调用accept()时telnet为何仍能建立连接?

以下是一段极简示例代码:

#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#include <stdio.h>
#include <stdlib.h>

#define NPROTO 10000
#define LOCALHOST "127.0.0.1"

int main(int argc, char *argv[])
{
 int                 sfd, epfd;
 struct sockaddr_in  so_address;
 struct epoll_event  ev, evs[1];

 sfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
 if(sfd < 0){
  fprintf(stderr, "socket() failed\n");
  exit(1);
 }
 epfd = epoll_create(1);
 if(epfd < 0){
  fprintf(stderr, "epoll_create() failed\n");
  exit(1);
 }

 memset(&so_address, 0, sizeof(so_address));
 so_address.sin_family       = AF_INET;
 so_address.sin_port         = htons(NPROTO);
 if(1 != inet_pton(AF_INET, LOCALHOST, (struct in_addr*)(&so_address.sin_addr.s_addr))){
  fprintf(stderr, "inet_pton() failed\n");
  exit(1);
 }

 if(bind(sfd, (struct sockaddr *)&so_address, sizeof(so_address)) < 0){
  fprintf(stderr, "bind() failed\n");
  exit(1);
 }

 if(listen(sfd, 4) < 0){
  fprintf(stderr, "listen() failed\n");
  exit(1);
 } 

 ev.data.fd = sfd;
 ev.events  = 0;
 epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);

 epoll_wait(epfd, evs, 1, -1);

 return 0;
}

该代码创建用于监听连接的Socket,并将其注册到epoll集合中,最后等待epoll集合上的事件。

注意,struct epoll_event的events标志被设置为0。使用上述示例代码时,telnet可成功连接至程序;若将该标志改为EPOLLIN,telnet则会提示“Connection refused”。


问题解答

TCP连接的建立是内核层面完成三次握手,和用户态程序是否调用accept()没有直接关联:

  1. 当程序调用listen(sfd, 4)后,内核会为这个监听Socket维护两个关键队列:

    • 半连接队列(SYN队列):存放已收到客户端SYN包、但还未完成三次握手的连接请求
    • 全连接队列(Accept队列):存放已完成TCP三次握手、等待用户态调用accept()取走的连接
  2. 当telnet发起连接请求时,只要全连接队列还有空闲位置(示例中backlog参数设为4),内核就会自动完成TCP三次握手,把连接放入全连接队列。此时telnet会收到内核返回的连接成功响应,认为连接已建立——哪怕用户态程序完全没调用accept()去处理这个连接(只是后续无法传输数据,因为没有创建用于数据交互的Socket)。

两种场景的差异原因

  • 当ev.events = 0时:epoll_wait不会监听任何事件,进程会一直阻塞在这个调用上,监听Socket始终处于有效监听状态。此时telnet发起连接,内核完成三次握手后将连接放入全连接队列,telnet自然显示连接成功。
  • 当ev.events = EPOLLIN时:监听Socket的EPOLLIN事件会在全连接队列有新连接时触发,epoll_wait返回后程序直接退出,监听Socket被内核自动关闭。此时telnet发起连接时,目标端口已经没有进程在监听,因此会收到“Connection refused”的提示。

内容的提问来源于stack exchange,提问作者jorge is not ai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 04:35:52