相关代码: windows 下 socket tcp 连接.
1. Windows 下需添加 Socket API
WSADATA wsaData;
WORD wVersion;
wVersion = MAKEWORD(2, 2); //声明调用不同的Winsock版本,(2.2)即2.2版
//绑定相应socket库
if(WSAStartup(wVersion, &wsaData) != NO_ERROR) //NO_ERROR==0
return false;
不同Winsock版本区别:1.1版只支持TCP/IP协议,2.0版可以支持多协议
(2.0版有良好的向后兼容性,任何使用1.1版的源代码、二进制文件、应用程序都可以不加修改地在2.0规范下使用。)
此外Winsock 2.0支持异步 1.1不支持异步.
结束时,需要解除与Socket库的绑定并且释放Socket库所占用的系统资源:
int WSACleanup (void);
*编译时如果报错:
错误 “error LNK2019: 无法解析的外部符号 _WSAStartup@8,该符号在函数 _main 中被引用”
错误原因:缺少相应库
解决方法:添加 #pragma comment(lib,“ws2_32.lib”)
2. 创建 socket 套接字
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
参数2:TCP连接设为 SOCK_STREAM
,UDP连接设为 SOCK_DGRAM
参数3:TCP连接设为 IPPROTO_TCP,UDP连接设为 IPPTOTO_UDP
返回值:成功返回套接字,错误返回 INVALID_SOCKET
3. 绑定服务器本地 IP 和 端口号
char ip[] = "192.168.0.110";
char port[] = "40000";
sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &serv_addr.sin_addr.S_un.S_addr);
serv_addr.sin_port = htons(atoi(port));
int ret = bind(fd, (sockaddr *)& serv_addr, sizeof(serv_addr));
返回值:成功返回 0,错误返回 -1
4. 设置监听
int ret = listen(fd, 1);
参数2:等待连接队列的最大长度
返回值:成功返回 0,错误返回 -1
5. 等待客户端连接
sockaddr_in client_addr; //接收客户端信息
socklen_t addrlen = sizeof(client_addr);
int clientfd = accept(fd, (struct sockaddr *) &client_addr, &addrlen);
等待客户端 connect()
返回值:成功返回描述符(通讯使用),错误返回INVALID_SOCKET
6. 发送 和 接收
send(clientfd, msg, msglen, 0);
返回值:成功发送数据个数,错误返回 SOCKET_ERROR
recv(clientfd, msg, msglen, 0);
返回值:成功接收数据个数,错误返回 SOCKET_ERROR
如果需要接收的数据过大,可以设置固定接收长度,循环接收。
7. 关闭套接字
closesocket(clientfd);
closesocket(fd);
8. setsockopt 设置
(1) 设置收发超时
int nNetTimeout = 5000; //ms
//设置接收时限
setsockopt(m_iConnfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout, sizeof(int));
//设置发送时限
setsockopt(m_iConnfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout, sizeof(int));
(2) 设置心跳
int keepalive = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive));
- 如果通信两端超过 2 个小时没有交换数据,那么开启 keep-alive 的一端会自动发一个 keep-alive 包给对端。
- 如果对端正常的回应 ACK 包,那么一切都没问题,再等个 2 小时后发包(如果这两个小时仍然没有数据交换)。
- 如果对端回应 RST 包,表明对端程序崩溃或重启,这边 socket 产生 ECONNRESET 错误,并且关闭。
- 如果对端一直没回应,这边会每 75 秒再发包给对端,总共发 8 次共 11 分钟 15 秒。最后 socket 产生 ETIMEDOUT 错误,并且关闭。或者收到 ICMP 错误,表明主机不可到达,则会产生 EHOSTUNREACH 错误。
windows 下心跳设置:
#include <MSTcpIP.h>
tcp_keepalive alive_in;
tcp_keepalive alive_out;
alive_in.keepalivetime = 8000; //检测心跳时间间隔
alive_in.keepaliveinterval = 400; //无回复重发时间间隔
alive_in.onoff = true;
unsigned long ulBytesReturn = 0;
WSAIoctl(dlg->listen_sock, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
连接断开后 send 和 recv 会即可返回,不会阻塞。
9. 失败报错
如果连接或通讯中函数返回错误,可通过 WSAGetLastError () 查询错误代码。
int errnum = WSAGetLastError();