/*
总结:
①无论阻塞还是非阻塞,select都不会立即返回,select就是用于非阻塞模型中的。
②将SOCKET置于非阻塞模式下时,处理连接或处理收发数据的Socket API都会立即返回。
③select会监视fd_set中的所有套接字,一旦有套接字发生IO事件(包括客户端的连接请求),select会立即返回,
并将fd_set中没有发生IO事件的套接字移除。由此可见,如果想让程序监视所有套接字的IO事件,应用程序应该保存所有的
套接字句柄,并在调用select之前将所有的套接字添加到fd_set中。
④阻塞模式下的accept调用返回的是阻塞模式的SOCKET,非阻塞模式返回的是非阻塞模式的SOCKET。
⑤对于连接请求和recv的套接字,应将其置于readSet中,即select中的第二个参数。
*/
#define _CRT_SECURE_NO_WARNINGS
#ifdef UNICODE
#undef UNICODE
#endif
#ifdef _UNICODE
#undef _UNICODE
#endif
#include <stdio.h>
#include <tchar.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
void ShowSystemError(DWORD dwError)
{
HLOCAL hlocal = NULL;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (PTSTR)&hlocal, 0, NULL);
_tprintf(_T("%s\n"), (TCHAR*)LocalLock(hlocal));
LocalFree(hlocal);
}
struct Node
{
SOCKET sc;
SOCKADDR_IN addr;
Node* next;
Node* prev;
};
int _tmain(int argc,TCHAR* argv[])
{
int nRet = 0;
WSADATA wsadata;
nRet = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (nRet != 0)
{
printf("WSAStartup调用失败,错误信息:");
ShowSystemError(nRet);
getchar();
return -1;
}
SOCKET serverSocket = INVALID_SOCKET;
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET)
{
printf("socket调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
goto END;
}
SOCKADDR_IN serverAddr;
ZeroMemory(&serverAddr, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;
serverAddr.sin_port = htons(8888);
if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) != 0)
{
printf("bind调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
goto END;
}
if (listen(serverSocket, 5) != 0)
{
printf("listen调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
goto END;
}
Node* pHead = new Node;
pHead->prev = pHead->next = NULL;
Node* pNewNode = new Node;
pNewNode->sc = serverSocket;
memcpy(&pNewNode->addr,&serverAddr,sizeof(SOCKADDR_IN));
pNewNode->prev = pHead;
pNewNode->next=pHead->next;
pHead->next = pNewNode;
FD_SET readSet;
SOCKET clientSocket;
SOCKADDR_IN clientAddr;
int nAddrLen;
Node* p;
Node* pTemp;
char recvBuf[1024];
while (true)
{
FD_ZERO(&readSet);
for (p = pHead->next; p != NULL; p = p->next)
FD_SET(p->sc,&readSet);
nRet = select(0,&readSet,NULL,NULL,NULL);
if (nRet < 0)
{
printf("select调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
continue;
}
for (p = pHead->next; p != NULL; p = p->next)
{
if (FD_ISSET(p->sc, &readSet))
{
if (p->sc == serverSocket)
{
clientSocket = INVALID_SOCKET;
ZeroMemory(&clientAddr,sizeof(SOCKADDR_IN));
nAddrLen = sizeof(SOCKADDR_IN);
clientSocket = accept(serverSocket,(SOCKADDR*)&clientAddr,&nAddrLen);
if (clientSocket == INVALID_SOCKET)
{
printf("accept调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
continue;
}
printf("%s:%d的客户成功连接到服务器。\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
pNewNode = new Node;
pNewNode->sc = clientSocket;
memcpy(&pNewNode->addr, &clientAddr, sizeof(SOCKADDR_IN));
pNewNode->prev = pHead;
pNewNode->next = pHead->next;
pHead->next->prev = pNewNode;
pHead->next = pNewNode;
}
else
{
ZeroMemory(recvBuf,sizeof(recvBuf));
nRet = recv(p->sc,recvBuf,sizeof(recvBuf),0);
if (nRet < 0)
{
printf("recv调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
if (closesocket(p->sc) != 0)
{
printf("closesocket调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
}
pTemp=p->prev;
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
p = pTemp;
}
else if (nRet == 0)
{
printf("%s:%d的客户主动关闭连接。\n", inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port));
if (closesocket(p->sc) != 0)
{
printf("closesocket调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
}
pTemp = p->prev;
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
p = pTemp;
}
else
{
//解析数据
printf("%s:%d的客户发送:%s\n", inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port),recvBuf);
}
}
}
}
}
END:
if (serverSocket != INVALID_SOCKET)
{
if (closesocket(serverSocket) != 0)
{
printf("closesocket调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
}
}
if (WSACleanup() != 0)
{
printf("WSACleanup调用失败,错误信息:");
ShowSystemError(WSAGetLastError());
}
getchar();
return 0;
}
基于select非阻塞模型的服务端程序示例(Winsock2实现)
最新推荐文章于 2023-06-01 08:00:00 发布