非阻塞 connect() 后需监听可写事件,再用 getsockopt 检查 so_error 为 0 才算成功;常见误判是把可读事件当连接完成或直接 read/recv 探测。

connect() 非阻塞后怎么判断连接成功还是失败
非阻塞 connect() 立即返回 -1,错误码是 EINPROGRESS,不是失败——这是最常误判的点。你得用 select()、poll() 或 epoll_wait() 监听 socket 的可写事件(注意:不是可读);触发后调用 getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) 检查 err 值,为 0 才算连上。
常见错误现象:
- 把可读事件当连接完成,导致读到空数据或 EAGAIN
- 忘记清 SO_ERROR,复用 socket 时残留旧错误
- 用 read() 或 recv() 直接试探,可能阻塞或返回 0(被对端 RST)
- Linux 下建议优先用
epoll_ctl(EPOLL_CTL_ADD)监听EPOLLOUT - 超时必须自己控制:
connect()启动后,epoll_wait()设置合理 timeout,避免无限等 - Windows 上对应的是
WSAEventSelect()+FD_WRITE,语义一致但行为略有差异
连接池里 socket 复用前必须做哪些清理
一个 socket 被归还到池中,不等于能直接 send()。TCP 连接处于 ESTABLISHED 状态,但内核缓冲区可能还有未读完的数据、应用层可能有半包、甚至对端已发 FIN 但本端还没 recv() 到。
关键动作是:归还前必须确保 socket 是“干净”的——即读缓冲区为空、无待处理应用协议状态、且确认对端没关闭连接。
- 调用
ioctl(fd, FIONREAD, &n)(Linux/Unix)或ioctlsocket()(Windows)检查是否有未读数据;若有,recv()到EAGAIN或0 - 不能只靠
shutdown(SHUT_WR)或close(),那会破坏连接池语义 - HTTP/1.1 场景下,要解析响应末尾的
Connection: keep-alive和Content-Length或Transfer-Encoding,否则可能把下个请求混进上个响应体 - 建议在归还前重置应用层 parser 状态(如 HTTP parser 的
http_parser_init())
epoll + 非阻塞 connect 的并发连接数瓶颈在哪
理论上限看 /proc/sys/fs/nr_open,但实际卡在三个地方:本地端口耗尽、TIME_WAIT 占用、内核连接跟踪表满。
立即学习“C++免费学习笔记(深入)”;
典型现象:connect() 频繁返回 EMFILE(文件描述符超限)或 EADDRNOTAVAIL(端口不可用),而不是 EINPROGRESS。
- 客户端端口范围默认是
32768–65535(约 32K),用完就卡住;可通过sysctl net.ipv4.ip_local_port_range扩大 - 大量短连接会堆积
TIME_WAIT,但连接池本身应复用连接,所以重点反而是:别让连接池“假死”——比如某个连接卡在recv()却没设超时,占着 fd 不放 - 高并发下
epoll_ctl()频繁增删 fd 开销明显,建议用EPOLLONESHOT避免重复通知,配合epoll_ctl(EPOLL_CTL_MOD)按需重置 - 每个 socket 要配独立的超时计时器(如 timerfd 或红黑树管理),不能全靠
epoll_wait()的全局 timeout
为什么用 std::shared_ptr 管理连接池容易出问题
表面看线程安全,实则掩盖了两个关键问题:生命周期和状态一致性。
比如一个 shared_ptr 被多个线程拷贝,其中一个线程调用 close() 后,其他线程再 send() 就是 EBADF;更隐蔽的是,shared_ptr 不保证 socket 内部状态(如是否已连接、是否正在读写)同步。
- 不要用
shared_ptr包裹裸 socket fd,改用 RAII 封装类(如class TcpConnection),内部管理 fd + 状态机 + buffer - 连接池分配时返回
unique_ptr或引用(配合池内锁),避免跨线程共享所有权 - 如果真要用智能指针,必须搭配
std::atomic<bool></bool>标记“是否可重用”,并在每次send()/recv()前检查 - 调试时多打日志:记录每次
connect()、epoll_wait()返回、send()实际字节数,否则吞吐量掉下去根本看不出哪一环卡住
连接池最难的从来不是建连,而是怎么让每个 socket 在「可用」「忙」「脏」「失效」之间准确切换——状态机比代码行数重要得多。









