网络编程之路---10

来源:互联网 发布:java的编程工具 编辑:程序博客网 时间:2024/05/21 19:06

开始之前先狠狠的鄙视自己一下,居然这么久都木有继续

select模型:防止在阻塞模式的套接字被锁死,避免在非阻塞套接字里重复检查WSAWOULDBLOCK错误.

流程:1:用FD_ZERO宏来初始化我们感兴趣的fd_set,也就是select函数的第二三四个参数。

2:用FD_SET宏来将套接字句柄分配给相应的fd_set。3:调用select函数。

4:用FD_ISSET对套接字句柄进行检查,如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么说明马上可以进行相应的IO操作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的fd_set里,那么说明当前数据已经来了,马上可以读取成功而不会被阻塞。


下面是select模型服务端代码:

#include <WinSock2.h>#include <iostream>#pragma comment(lib, "WS2_32.lib")#define BUF_SIZE 64#define DATA_BUFSIZE 1024#define PORT 9990typedef struct _SOCKET_INFORMATION{CHAR Buffer[DATA_BUFSIZE]; //收发数据的缓冲区WSABUF DataBuf;           //定义发送和接收数据的结构体SOCKET socket;            //与客户进行通信的socketDWORD ByteSEND;           //socket发送的字节数DWORD ByteRECV;          //socket接收的字节数}SOCKET_INFORMATION , *LPSOCKET_INFORMATION;DWORD TotalSockets; //正在使用的socket总数LPSOCKET_INFORMATION SocketArray[FD_SETSIZE];BOOL CreateSocketInformation(SOCKET s) //创建socket信息{LPSOCKET_INFORMATION SI;if ( (SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL ){printf("GlobalAlloc failed with error %d\n", GetLastError());return FALSE;}SI->socket = s;SI->ByteSEND = 0;SI->ByteRECV = 0;SocketArray[TotalSockets] = SI;TotalSockets++;return TRUE;}void FreeSocketInformation(DWORD index) //释放socket信息{LPSOCKET_INFORMATION SI = SocketArray[index];DWORD i;closesocket(SI->socket);GlobalFree(SI);for (i = index; i < TotalSockets; i++){SocketArray[i] = SocketArray[i + 1];}TotalSockets--;}int main(int argc, char *argv[]){SOCKET ListenSocket;SOCKET AcceptSocket;SOCKADDR_IN InternetAddr;WSADATA wsaData;INT ret; //用来检查各种返回值结果的FD_SET WriteSet;FD_SET ReadSet;DWORD Total = 0;DWORD SendBytes = 0;DWORD RecvBytes = 0;if ((ret = WSAStartup(0X0202, &wsaData)) != 0){printf("WSAstarup() failed\n");WSACleanup();return -1;}if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET){printf("WSAsocket() failed\n");closesocket(ListenSocket);WSACleanup();return -1;}InternetAddr.sin_family = AF_INET;InternetAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);InternetAddr.sin_port = htons(PORT);if (bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR){printf("bind error\n");closesocket(ListenSocket);WSACleanup();return -1;}if (listen(ListenSocket, 5)){printf("listen failed\n");return -1;}ULONG noBlock = 1; //设置为非阻塞模式if (ioctlsocket(ListenSocket, FIONBIO, &noBlock) == SOCKET_ERROR){printf("ioctlsocket failed");return -1;}CreateSocketInformation(ListenSocket);while(TRUE){//初始化我们感兴趣的fe_setFD_ZERO(&ReadSet);FD_ZERO(&WriteSet);//用FD_SET宏来将套接字句柄分配给相应的fd_set//如果想要检查一个套接字是否有数据需要接收,可以用FD_SET宏把套接字句柄加入可读性检查队列中FD_SET(ListenSocket, &ReadSet);//循环把所有的套接字都放进对应的集合?for (DWORD i = 0; i < TotalSockets; ++i){LPSOCKET_INFORMATION socket_info = SocketArray[i];FD_SET(socket_info->socket, &ReadSet);FD_SET(socket_info->socket, &WriteSet);}//调用select函数,如果该套接字没有数据要接收,select函数会把该套接字从可读性列表中删除if ( (Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR){printf("select failed\n");return -1;}//用FD_ISSET对套接字句柄进行检查for (DWORD i = 0; i < TotalSockets; ++i){LPSOCKET_INFORMATION socket_info = SocketArray[i];if (FD_ISSET(socket_info->socket, &ReadSet)){if (socket_info->socket == ListenSocket){Total--; //就绪的socket-1//参数addr所指向的套接字地址结构中将存放客户机的相关信息,addrLen指针将描述套接字地址结构长度//通常情况下服务器对这些信息不是很感兴趣,故可以设置为NULLif ( (AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET){noBlock = 1;if (ioctlsocket(AcceptSocket, FIONBIO, &noBlock) == SOCKET_ERROR){printf("ioctlsocket failed\n");return -1;}//把accept放入数组中if (CreateSocketInformation(AcceptSocket) == FALSE)return -1;}else{if (GetLastError() != WSAEWOULDBLOCK){printf("accept failed\n");return -1;}}}else //有可读的数据{if (FD_ISSET(socket_info->socket, &ReadSet)){Total--; //就绪状态的socket--//初始化缓冲区memset(socket_info->Buffer, 0 , DATA_BUFSIZE);socket_info->DataBuf.buf = socket_info->Buffer;socket_info->DataBuf.len = DATA_BUFSIZE;DWORD Flags = 0;//重叠模型 WSARecv接收数据if (WSARecv(socket_info->socket, &(socket_info->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL)){if (WSAGetLastError() != WSAEWOULDBLOCK){printf("wsarecv failed\n");return -1;}continue;}else{socket_info->ByteRECV = RecvBytes; //记录接收数据的字节数if (RecvBytes == 0){FreeSocketInformation(i);continue;}else{printf(socket_info->DataBuf.buf);printf("\n");}}}}}else{//表明有数据可以发送if (FD_ISSET(socket_info->socket, &WriteSet)){//初始化缓冲区位置;socket_info->DataBuf.buf = socket_info->Buffer + socket_info->ByteSEND;//初始化缓冲区长度socket_info->DataBuf.len = socket_info->ByteRECV - socket_info->ByteSEND;if (socket_info->DataBuf.len > 0){if (WSASend(socket_info->socket, &(socket_info->DataBuf), 1, &SendBytes, 0, NULL, NULL)){if (WSAGetLastError() != WSAEWOULDBLOCK){printf("WSASend failed\n");FreeSocketInformation(i);}continue;}else{socket_info->ByteSEND += SendBytes;if (socket_info->ByteSEND == socket_info->ByteRECV) //如果从客户端收到的数据都已经发送到客户端{socket_info->ByteRECV = 0;socket_info->ByteSEND = 0;}}}}}}}closesocket(ListenSocket);closesocket(AcceptSocket);WSACleanup();return 0;}

表示根据前面的几部流程还是蛮轻松的。。


0 0