网络编程 select模型
创始人
2024-04-30 20:38:14

       

目录

select模型详解

        select函数解释

整体代码


        select模型在代码上和c/s模型的前面一部分是一样的,可以去看 这个icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_62859191/article/details/128397927?spm=1001.2014.3001.5501,相同的代码如下

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include
#include
#pragma comment(lib,"ws2_32.lib")int main()
{//第一步 打开网络库/校验版本WORD wdVersion = MAKEWORD(2, 2);WSADATA  wdSockMsg;int nRes = WSAStartup(wdVersion, &wdSockMsg);if (nRes != 0){printf("打开网络库失败\n");}if (wdSockMsg.wVersion != HIBYTE(2) || wdSockMsg.wVersion != LOBYTE(2)){WSACleanup();printf("版本不对\n");return 0;}//第二步 创建socketSOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (socketServer == INVALID_SOCKET){int a = WSAGetLastError();//获取错误码printf("创建socket出错\n");WSACleanup();return 0;}//第三步 绑定ip地址和端口号struct sockaddr_in si;si.sin_family = AF_INET;si.sin_port = htons(12332);si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if(SOCKET_ERROR == bind(socketServer, (const sockaddr*)&si, sizeof(si))){int a = WSAGetLastError();closesocket(socketServer);WSACleanup();printf("创建socket出错\n");return 0;}//第四步 开始监听if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)){int a = WSAGetLastError();closesocket(socketServer);WSACleanup();printf("监听出错\n");return 0;}closesocket(socketServer);WSACleanup();return 0;
}

select模型详解

        作用:select模型使用来解决c/s模型中收发消息时阻塞的问题,用在服务器上

        需要用到 fd_set 网络中定义好的结构体,结构体原型

typedef struct fd_set {u_int  fd_count;SOCKET fd_array[FD_SETSIZE];
} fd_set, FD_SET, *PFD_SET, *LPFD_SET;

系统也给 fd_set 结构体提供了4个操作宏

FD_ZERO:把fd_set结构体中的所有数据清零

FD_SET:在fd_set结构体中添加一个参数

FD_CLR:删除指定的socket变量

FD_ISSET:判断fd_set结构体中是否有指定的socket变量,有返回非0,没有返回0

        ##代码样例

//创建一个存放socket的结构体fd_set clientSockets;FD_ZERO(&clientSockets);//把集合中的所有数据清零FD_SET(socketServer, &clientSockets);//添加元素进集合//删除的时候一定要closeFD_CLR(socketServer, &clientSockets);//删除集合中指定的元素//closesocket(socketServer);int a = FD_ISSET(socketServer, &clientSockets);//判断集合中是否有指定的元素 存在返回非0,不在返回0

        select函数解释

该函数确定一个或多个套接字的状态(如有必要)等待执行同步 I/O。函数原型

int WSAAPI select([in]      int           nfds,[in, out] fd_set        *readfds,[in, out] fd_set        *writefds,[in, out] fd_set        *exceptfds,[in]      const timeval *timeout
);

参数1 nfds:填0,表示可以兼容旧版本

参数2 readfds:检查是否有可读的socket,即客户端发来消息,对应的socket就会被设置

参数3 writefds:检查是否有可写的socket,就是可以给哪些客户端套接字发消息,只要链接成功建立起来了,那该客户端套接字就是可写的。

参数4 exceptfds:检查套接字上的异常错误,用法跟2,3一样

参数5 timeout:这是一个结构体对象,结构体原型如下,该参数可以设置最大等待时间,结构体中的两个成员,tv_sec表示秒,tv_usec表示微妙,设置为00时,非阻塞状态,立刻返回,设置为1,1表示在无客户端响应下等待1秒1微妙,如果填入NULL,会完全阻塞,直到客户端有响应

typedef struct timeval {long tv_sec;long tv_usec;
} TIMEVAL, *PTIMEVAL, *LPTIMEVAL;

        返回值:该函数如果返回0,表示在客户端等待时间内没有反应,如果返回大于0的数,表示有客户端请求交流,如果返回 SOCKET_ERROR,表示发生了错误,使用WSAGetLastError()可以获取错误码

        在使用参数4时,需要用到函数 getsockopt ,该函数可以获取到socket发生错误时的错误信息,函数原型,及使用样例

int WSAAPI getsockopt([in]      SOCKET s,[in]      int    level,[in]      int    optname,[out]     char   *optval,[in, out] int    *optlen
);
//使用样例
char str[100] = {0};
int len = 99;
getsockopt(errorSockets.fd_array[i], SOL_SOCKET, SO_ERROR, str, &len)

        错误信息返回会给到字符数组str,如果可以找到错误,返回0,否则返回SOCKET_ERROR

整体代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include
#include
#pragma comment(lib,"ws2_32.lib")int main()
{printf("**********服务器***********\n");//第一步 打开网络库/校验版本WORD wdVersion = MAKEWORD(2, 2);WSADATA  wdSockMsg;int nRes = WSAStartup(wdVersion, &wdSockMsg);if (nRes != 0){printf("打开网络库失败\n");}if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion)){WSACleanup();printf("版本不对\n");return 0;}//第二步 创建socketSOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (socketServer == INVALID_SOCKET){int a = WSAGetLastError();//获取错误码printf("创建socket出错\n");WSACleanup();return 0;}//第三步 绑定ip地址和端口号struct sockaddr_in si;si.sin_family = AF_INET;si.sin_port = htons(12332);si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if(SOCKET_ERROR == bind(socketServer, (const sockaddr*)&si, sizeof(si))){int a = WSAGetLastError();closesocket(socketServer);WSACleanup();printf("创建socket出错\n");return 0;}//第四步 开始监听if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)){int a = WSAGetLastError();closesocket(socketServer);WSACleanup();printf("监听出错\n");return 0;}//创建一个存放socket的结构体fd_set allSockets;FD_ZERO(&allSockets);//把集合中的所有数据清零//把服务器装进去FD_SET(socketServer, &allSockets);//添加元素进集合删除的时候一定要close//FD_CLR(socketServer, &clientSockets);//删除集合中指定的元素closesocket(socketServer);//int a = FD_ISSET(socketServer, &clientSockets);//判断集合中是否有指定的元素 存在返回非0,不在返回0while (1){fd_set readSockets = allSockets;//第二个参数的中间变量fd_set writeSockets = allSockets;//第三个参数的中间变量fd_set errorSockets = allSockets;//第四个参数的中间变量FD_CLR(socketServer, &writeSockets);FD_CLR(socketServer, &errorSockets);struct timeval st;st.tv_sec = 3;st.tv_usec = 0;int sRes = select(0, &readSockets, &writeSockets, NULL, &st);//该函数会改变tempSockets中的元素if (sRes == 0){//客户端没有响应continue;}else if (sRes > 0){//错误socket处理for (u_int i = 0; i < errorSockets.fd_count; i++){char str[100] = { 0 };int len = 99;//参数4:通过参数2进行参数传址获取错误码if (SOCKET_ERROR == getsockopt(errorSockets.fd_array[i], SOL_SOCKET, SO_ERROR, str, &len)){printf("无法得到错误信息\n");}}//服务器发送消息for (u_int i = 0; i < writeSockets.fd_count; i++){if (SOCKET_ERROR == send(writeSockets.fd_array[i], "OK", 2, 0))//客户端正常关闭也时返回SOCKET_ERROR{printf("发送出错\n");int a = WSAGetLastError();//获取错误码}}//客户端有发消息for (u_int i = 0; i < readSockets.fd_count; i++){if (readSockets.fd_array[i] == socketServer)//表示有客户端请求连接{//创建客户端socketSOCKET socketClient = accept(socketServer, NULL, NULL);if (socketClient == INVALID_SOCKET){//链接出错continue;}//否则把该socket放入tempSocket数组中FD_SET(socketClient, &allSockets);printf("客户端连接成功\n");}else//否则就是有客户端发送消息,需要接收消息{char buf[1500] = { 0 };int nRecv = recv(readSockets.fd_array[i], buf, 1499, 0);if (nRecv == 0)//表示客户端下线{SOCKET socketTemp = readSockets.fd_array[i];printf("客户端下线\n");FD_CLR(readSockets.fd_array[i], &allSockets);closesocket(socketTemp);}else if (nRecv > 0)//接收到客户端发来的消息{printf("%d	%s\n", nRecv, buf);}else//SOCKET_ERROR 非正常关闭{SOCKET socketTemp = readSockets.fd_array[i];printf("客户端被迫退出\n");int a = WSAGetLastError();//获取错误码FD_CLR(readSockets.fd_array[i], &allSockets);closesocket(socketTemp);}}}}else{//发生错误break;}}//释放所有的socketfor (u_int i = 0; i < allSockets.fd_count; i++){closesocket(allSockets.fd_array[i]);}closesocket(socketServer);WSACleanup();return 0;
}

相关内容

热门资讯

代码里的未寄书 (来源:嘉兴日报)转自:嘉兴日报   ■张志翔  亲情是生命里最温润的底色,它藏在时光的褶皱里,无声...
最新或2023(历届)军人家属... 要用三年时间提高低工资收入人群的收入,使其达到平均水平。 工资制度改革,并不是简单增加工资,而是重在...
最新或2023(历届)军费主要... 要用三年时间提高低工资收入人群的收入,使其达到平均水平。 工资制度改革,并不是简单增加工资,而是重在...
最新或2023(历届)军队福利... 要用三年时间提高低工资收入人群的收入,使其达到平均水平。 工资制度改革,并不是简单增加工资,而是重在...
最新或2023(历届)各国军队... 要用三年时间提高低工资收入人群的收入,使其达到平均水平。 工资制度改革,并不是简单增加工资,而是重在...