Python 3 中 socket.socket() 默认使用 AF_INET 和 SOCK_STREAM,因为官方文档明确规定其默认参数为 family=AF_INET、type=SOCK_STREAM,对应 IPv4 地址族和 TCP 流式传输,确保开箱即用的 TCP 连接能力。

socket() 创建套接字时为什么必须指定 AF_INET 和 SOCK_STREAM
因为 socket() 不是“开箱即用”的接口,它需要明确通信域(address family)和传输类型。用 AF_INET 表示 IPv4 地址族,SOCK_STREAM 表示 TCP 流式连接——这两个组合才是传统“服务端监听”的基础。如果错用 AF_INET6 却没配置双栈,或误选 SOCK_DGRAM(UDP),后续 bind() 可能成功但 accept() 永远阻塞或直接失败。
常见错误现象:bind(): Invalid argument 或程序启动后无法被 telnet 连通,大概率是协议族与 socket 类型不匹配。
- Linux 下可检查
/proc/sys/net/ipv4/ip_forward是否为 0(避免干扰),但不是必须项 - Windows 需提前调用
WSAStartup(),否则所有 socket 函数返回INVALID_SOCKET - 返回值必须检查:
socket()失败返回 -1(Unix/Linux)或INVALID_SOCKET(Windows)
bind() 绑定地址时 INADDR_ANY 和 127.0.0.1 的区别
INADDR_ANY(即 0.0.0.0)表示监听本机所有 IPv4 网卡的 0.0.0.0:端口,外部机器能通过任意网卡 IP 访问;而 127.0.0.1 仅允许本地 loopback 连接,其他机器连不上。开发调试常用后者,上线部署通常用前者。
容易踩的坑:绑定 127.0.0.1 后用手机或另一台电脑 telnet,必然超时——不是代码错,是地址绑死了。
立即学习“C++免费学习笔记(深入)”;
- 结构体
sockaddr_in中sin_port必须用htons(端口号)转换,否则端口值错乱(如传 8080 得到 8080 - 端口小于 1024 需 root / Administrator 权限,否则
bind()返回 -1 并设errno = EACCES - 若提示
Address already in use,加setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))
accept() 阻塞等待连接时如何避免主线程卡死
默认 accept() 是阻塞的,没有客户端连入就一直挂起。这不是 bug,是设计使然。但如果你希望服务端能同时处理信号、定时任务或多个客户端,就不能只靠一个 accept()。
最轻量的解法是用 select() 或 poll() 监听监听套接字是否就绪,再调 accept()。不用线程、不引入 epoll/kqueue,兼容性好。
-
select()第一个参数是最大 fd + 1,别直接传监听 fd - 每次调
select()前要重置fd_set,否则第二次会失效 -
accept()成功返回新 socket fd,这个 fd 才用于recv()/send(),原监听 fd 保持不变继续accept() - 客户端断开时
recv()返回 0,不是错误,应主动close()对应 fd
recv() 读不到完整 HTTP 请求?别直接当成字符串处理
TCP 是字节流,recv() 只保证最多读 n 字节,不保证一次读完一个“请求”。比如发 GET / HTTP/1.1\r\nHost: a.com\r\n\r\n,可能第一次只读到前 20 字节,第二次才补上剩余部分。直接 recv(buf, 1024, 0) 然后当 C 字符串用,极易越界或截断。
简单服务端可按行解析(找 \r\n\r\n),但必须自己维护缓冲区并处理粘包/半包。不要依赖 “一次 recv 就是一次请求” 这种假设。
- 声明缓冲区至少 4096 字节,用
std::vector<char>动态追加比固定数组更安全 - 每次
recv()后检查返回值:errno),== 0 表示对端关闭 - HTTP 头部结束标记是连续两个
\r\n,但中间可能跨两次recv(),得拼起来搜 - 生产环境务必设
SO_RCVTIMEO,否则恶意客户端不发数据,fd 就永远卡在recv()
accept() 和 recv() 在各种异常网络条件下不崩、不泄漏 fd、不无限等待。很多初学者的“能跑通”版本,在客户端强制断网或快速重连几十次后就 fd 耗尽或 core dump 了。


















