客户端代码:
/* * 文件:Select(客户端) * 说明:项目中需包含 Ws2_32.lib,或使用 #pragma comment(lib, "*.lib") */ #include <stdio.h> #include <WinSock2.h> #define LENGTH 128 #define PORT_NUM 8086 #define MAX_DELAY 3000 // 比简单示例多出的部分 #define EXIT_WITH_MESSAGE(content) \ do \ { \ WSACleanup(); \ printf_s(content); \ system("pause"); \ return EXIT_FAILURE; \ } \ while (FALSE); int main() { int ret, len; WORD wVersion; // 网络传输版本 WSADATA wsaData; // WSADATA char msg[LENGTH]; struct sockaddr_in server; // 服务器信息 SOCKET sockClient = INVALID_SOCKET; // 待创建的套接字 /* 比简单示例多出的部分 */ struct timeval tv; struct fd_set fdset; wVersion = MAKEWORD(2, 2); ret = WSAStartup(wVersion, &wsaData); // 初始化 WinSock DLL if (ret != 0) { // 告诉用户不能获取可用的 WinSock DLL EXIT_WITH_MESSAGE("WSAStartup Failed, cannot find a usable winsock dll!\n"); } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { // 告诉用户版本不正确 EXIT_WITH_MESSAGE("Error version!\n"); } sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == sockClient) { // 套接字创建失败 EXIT_WITH_MESSAGE("Invalid socket!\n"); } server.sin_family = AF_INET; server.sin_port = htons(PORT_NUM); server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ret = connect(sockClient, (struct sockaddr *)&server, sizeof(struct sockaddr)); if (SOCKET_ERROR == ret) { // 连接失败 closesocket(sockClient); EXIT_WITH_MESSAGE("Connect failed!\n"); } do { /* 比简单示例多出的部分 */ FD_ZERO(&fdset); FD_SET(sockClient, &fdset); tv.tv_sec = 0; tv.tv_usec = MAX_DELAY; ret = select(0, NULL, &fdset, NULL, &tv); if (ret <= 0) { // 选择失败 closesocket(sockClient); EXIT_WITH_MESSAGE("Select failed!\n"); } else if (!FD_ISSET(sockClient, &fdset)) { // 设置失败 closesocket(sockClient); EXIT_WITH_MESSAGE("FD isn't set!\n"); } printf_s("Your reply: "); /* 此为测试代码,因为有严格的超时时间限制,如果在最大延时内收不到则认为设备掉线 / strcpy_s(msg, LENGTH, "Hello, SweetLover!"); */ gets_s(msg, LENGTH); // send 函数将会把消息发送到已连接的套接字上,而 sendto 则是发送到指定套接字上 len = strlen(msg) + 1; ret = send(sockClient, msg, len, 0); if (ret < 0 || ret > len) { // 发送有误 closesocket(sockClient); EXIT_WITH_MESSAGE("Send message error!\n"); } if (strcmp(msg, "Quit") == 0) break; /* 比简单示例多出的部分 */ FD_ZERO(&fdset); FD_SET(sockClient, &fdset); tv.tv_sec = 0; tv.tv_usec = MAX_DELAY; ret = select(0, &fdset, NULL, NULL, &tv); if (ret <= 0) { // 选择失败 closesocket(sockClient); EXIT_WITH_MESSAGE("Select failed!\n"); } else if (!FD_ISSET(sockClient, &fdset)) { // 设置失败 closesocket(sockClient); EXIT_WITH_MESSAGE("FD isn't set!\n"); } memset(msg, 0, LENGTH); ret = recv(sockClient, msg, LENGTH, 0); if (SOCKET_ERROR == ret) { // 接受有误 closesocket(sockClient); EXIT_WITH_MESSAGE("Receive message error!\n"); } else if (0 == ret) { // 连接断开 closesocket(sockClient); EXIT_WITH_MESSAGE("The connection closed!\n"); } printf_s("Server: %s\n", msg); } while (strcmp(msg, "Quit")); printf_s("Exit connection!\n"); closesocket(sockClient); WSACleanup(); system("pause"); return EXIT_SUCCESS; }
服务器端代码:
/* * 文件:Select(服务器端) * 说明:项目中需包含 Ws2_32.lib,或使用 #pragma comment(lib, "*.lib") */ #include <stdio.h> #include <WinSock2.h> #define LENGTH 128 #define PORT_NUM 8086 #define MAX_DELAY 3000 // 比简单示例多出的部分 #define EXIT_WITH_MESSAGE(content) \ do \ { \ WSACleanup(); \ printf_s(content); \ system("pause"); \ return EXIT_FAILURE; \ } \ while (FALSE); int main() { int ret, len; WORD wVersion; // 网络传输版本 WSADATA wsaData; // WSADATA char msg[LENGTH]; struct sockaddr_in server, client; // 服务器信息,客户端信息 SOCKET sockServer = INVALID_SOCKET, sockListener = INVALID_SOCKET; // 服务器套接字,监听套接字 /* 比简单示例多出的部分 */ struct timeval tv; struct fd_set fdset; wVersion = MAKEWORD(2, 2); ret = WSAStartup(wVersion, &wsaData); // 初始化 WinSock DLL if (ret != 0) { // 告诉用户不能获取可用的 WinSock DLL EXIT_WITH_MESSAGE("WSAStartup Failed, cannot find a usable winsock dll!\n"); } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { // 告诉用户版本不正确 EXIT_WITH_MESSAGE("Error version!\n"); } sockListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == sockListener) { // 套接字创建失败 EXIT_WITH_MESSAGE("Invalid listener socket!\n"); } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(PORT_NUM); ret = bind(sockListener, (struct sockaddr *)&server, sizeof(struct sockaddr_in)); if (SOCKET_ERROR == ret) { // 绑定端口失败 closesocket(sockListener); EXIT_WITH_MESSAGE("Bind error!\n"); } ret = listen(sockListener, SOMAXCONN); if (SOCKET_ERROR == ret) { // 监听失败 closesocket(sockListener); EXIT_WITH_MESSAGE("Listen error!\n"); } len = sizeof(struct sockaddr_in); sockServer = accept(sockListener, (struct sockaddr*)&client, &len); if (INVALID_SOCKET == sockServer) { // 接收失败 closesocket(sockListener); EXIT_WITH_MESSAGE("Accept error!\n"); } do { /* 比简单示例多出的部分 */ FD_ZERO(&fdset); FD_SET(sockServer, &fdset); tv.tv_sec = 0; tv.tv_usec = MAX_DELAY; ret = select(0, &fdset, NULL, NULL, &tv); if (ret <= 0) { // 选择失败 closesocket(sockServer); EXIT_WITH_MESSAGE("Select failed!\n"); } else if (!FD_ISSET(sockServer, &fdset)) { // 设置失败 closesocket(sockServer); EXIT_WITH_MESSAGE("FD isn't set!\n"); } memset(msg, 0, LENGTH); ret = recv(sockServer, msg, LENGTH, 0); if (SOCKET_ERROR == ret) { // 接受有误 closesocket(sockServer); EXIT_WITH_MESSAGE("Receive message error!\n"); } else if (0 == ret) { // 连接断开 closesocket(sockServer); EXIT_WITH_MESSAGE("The connection closed!\n"); } printf_s("Client: %s\n", msg); /* 比简单示例多出的部分 */ FD_ZERO(&fdset); FD_SET(sockServer, &fdset); tv.tv_sec = 0; tv.tv_usec = MAX_DELAY; ret = select(0, NULL, &fdset, NULL, &tv); if (ret <= 0) { // 选择失败 closesocket(sockServer); EXIT_WITH_MESSAGE("Select failed!\n"); } else if (!FD_ISSET(sockServer, &fdset)) { // 设置失败 closesocket(sockServer); EXIT_WITH_MESSAGE("FD isn't set!\n"); } printf_s("Your reply: "); gets_s(msg, LENGTH); len = strlen(msg) + 1; ret = send(sockServer, msg, len, 0); if (ret <= 0 || ret > len) { // 发送有误 closesocket(sockServer); EXIT_WITH_MESSAGE("Send message error!\n"); } } while (TRUE); printf_s("Exit connection!\n"); closesocket(sockServer); closesocket(sockListener); WSACleanup(); system("pause"); return EXIT_SUCCESS; }
作用:http://baike.baidu.com/subview/621719/11844440.htm?fr=aladdin
1.原型:int select (int maxfd + 1,fd_set *readset,fd_set *writeset, fd_set *exceptset,const struct timeval * timeout);
2.参数:
总结:
1.编程角度上,Select 的关键就是能否掌握对该函数的使用。
2.Select 中比较重要的结构体是 fd_set 和 timeval,在调用 select 前,要初始化:
FD_ZERO(&fdset);
FD_SET(sockServer, &fdset);
具体阐释:
1.struct fd_set fdset; // 文件(这里的文件指的是套接字 socket)描述集合;
一个文件集合中可以包含多个元素,即:element∈fd_set;
2.FD_ZERO(&fdset); // 初始化文件描述集合为空,没有任何元素
3.FD_SET(sockServer, &fdset); // 将元素放入集合中,可以后置多个
4.详解
函数作用:选择函数确定一个或多个套接字的状态,必要时等待执行同步I / O。
函数参数:
(1)nfds[in]:Windows 下可忽略,只是用于兼容 Berkeley 套接字(Linux、Android)时才会用到。
(2)readfds[in & out]:用于检测可读性的文件描述集合,其他两个是用于检测可写性和异常的文件描述集合。
(3)timeout[in]:最大等待时间。表现为:最大等待时间内,套接字个数检测结果不为空时立刻返回个数,为空则在超时后返回0,错误返回 SOCKET_ERROR。
返回值:三个检测集合中的套接字个数,超时返回0,错误返回 SOCKET_ERROR。
5.小示例:
FD_ZERO(&fd_read); // fd_read.count = 0
FD_SET(s1, &fd_read); // fd_read.count = 1
FD_SET(s2, &fd_read); // fd_read.count = 2
// 在 tv 时间内,服务器的两个套接字 s1 和 s2 只有,s1 来了数据(可读缓冲区不为空)
ret = select(0, &fd_read, NULL, NULL, &tv); // 因此执行后,ret = 1,fd_read.count = 1
if (FD_ISSET(s1, &fd_read)){// recv/recvfrom 接收数据,并且做出处理} // 这里的 if 会被执行
if (FD_ISSET(s2, &fd_read)){// ... } // 这里的 if 不会被执行
同理:如果可写套接字的缓冲区为空,那么检测后就是可写状态