tcp连接三步:socket()创建套接字、设置sockaddr_in地址结构、connect()发起连接;需注意wsastartup()初始化(windows)、inet_pton()转ip、htons()转端口、af_inet/sock_stream固定搭配;收发须用send()/recv()并循环处理。

怎么用 socket() 建立 TCP 连接
核心就是三步:创建套接字、设置服务器地址、调用 connect()。Windows 和 Linux 的 API 几乎一致,但头文件和初始化方式不同——Linux 直接用,Windows 必须先调 WSAStartup(),否则 socket() 返回 INVALID_SOCKET。
常见错误是把 IP 地址写成字符串直接传给 connect(),实际要先用 inet_pton()(推荐)或 inet_addr() 转成二进制格式,再填入 sockaddr_in 的 sin_addr.s_addr 字段。
-
AF_INET和SOCK_STREAM是固定搭配,别用AF_INET6或SOCK_DGRAM - 端口号必须用
htons()转换,否则大小端问题会导致连不上(尤其跨平台时) - 连接失败时,
connect()返回 -1,Linux 查errno,Windows 查WSAGetLastError()
如何安全地收发数据:用 send() 和 recv() 而不是 write()/read()
send() 和 recv() 是 socket 专用接口,支持标志位控制(比如 MSG_NOSIGNAL 防止 SIGPIPE),而 write()/read() 在某些系统上对 socket 行为不一致,甚至在 Windows 上根本不可用。
关键点在于:它们不保证一次调用就把所有数据发完或收完。比如你传 1024 字节,send() 可能只发出 300;recv() 也可能只返回前 200 字节。所以必须循环处理:
立即学习“C++免费学习笔记(深入)”;
int total = 0;
while (total < len) {
int n = send(sock, buf + total, len - total, 0);
if (n <= 0) break; // 错误或断开
total += n;
}- 发送时建议加
MSG_NOSIGNAL(Linux)避免进程被终止 - 接收时若返回 0,表示对端已关闭连接(不是错误)
- 不要假设
recv()会等满缓冲区再返回——它只要收到一点就可能返回
为什么 close() 或 closesocket() 后还收得到数据
调用关闭函数只是把本端的读/写通道标记为关闭,并不立即清空内核缓冲区。对端发来的数据仍可能在 TCP 接收队列里,后续 recv() 还能取到,直到队列为空才真正结束。
更麻烦的是:如果没等对方数据收完就退出,可能丢掉最后几字节。典型表现是服务端发了 "OK\n",客户端却只收到 "O" 就结束了。
- 正确做法是先
shutdown(sock, SHUT_WR),再循环recv()直到返回 0 - Windows 必须用
closesocket(),不能用close(),否则句柄泄漏 - Linux 下
close()默认行为是SHUT_RDWR,但语义不如显式shutdown()清晰
调试连不上时该看哪几个地方
90% 的连接失败不是代码问题,而是环境或配置卡住。优先检查这几点:
- 目标 IP 是否可达?用
ping测试(注意有些服务器禁 ping,但不妨碍 TCP) - 端口是否开放?用
telnet host port或nc -zv host port直接验证 - 防火墙是否拦截?Linux 查
iptables -L,Windows 检查“高级安全 Windows 防火墙” - 服务器进程是否真的在监听?用
netstat -tuln | grep :port(Linux)或netstat -ano | findstr :port(Windows)
如果 connect() 卡住几秒才失败,大概率是路由不通或目标主机不存在;如果立刻返回错误码 111(Linux)或 10061(Windows),说明连接被拒绝——服务没起来,或者 bind 的是 127.0.0.1 而你从外网连。











