基于select非阻塞模型的服务端程序示例(Winsock2实现)

/*
总结:
	①无论阻塞还是非阻塞,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;
}

猜你喜欢

转载自blog.csdn.net/csdn_gddf102384398/article/details/84501448