Skip to content

select模型

Published: at 02:28 PM | 3 min read

需要注意的是要将listenSock设置为非阻塞模式,这样在while(1)循环中accept才不会被阻塞。同时listenSock也要set到fdread中,如果不这样做的话,select只能监视到客户端的socket,当客户端没有进行任务IO操作的时候select返回值为SOCKET_ERROR会一直进行循环检测,这样是没必要的。

如果有listenSock在fdread中,而又没有新的客户端接入的时候select会阻塞的,只要有一个客户端进行了IO操作或者有新客户端接入select就会变成非阻塞继续进行下面的处理。

在每次FD_SET之前必须FD_ZERO,所以这里使用了一个list来保存SOCKET,在下一次循环的时候重新FD_SET一遍list中的所有SOCKET。


// TcpDemo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <sstream>
#include <list>

#pragma comment(lib, "Ws2_32.lib")

void handleConn(std::list<SOCKET> &clientSocks, fd_set &fdread);
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	SOCKET listenSock;
	SOCKADDR_IN serverAddr;
	SOCKADDR_IN clientAddr;
	fd_set fdread;
	int port = 5100;

	WSAStartup(MAKEWORD(2, 2), &wsaData);

	listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == listenSock)
	{
		std::cout << "socket failed, errcode:" << WSAGetLastError() << std::endl;
		return 0;
	}
	unsigned long iMode = 1;
	ioctlsocket(listenSock, FIONBIO, &iMode);

	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(port);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	int iret = bind(listenSock, (sockaddr*)&serverAddr, sizeof(serverAddr));
	if (SOCKET_ERROR == iret)
	{
		std::cout << "bind failed, errcode:" << WSAGetLastError() << std::endl;
		closesocket(listenSock);
		return 0;
	}
	
	iret = listen(listenSock, SOMAXCONN);
	if ( SOCKET_ERROR == iret)
	{
		std::cout << "listen failed, errcode:" << WSAGetLastError() << std::endl;
		closesocket(listenSock);
		return 0;
	}

	int clientAddrLen = sizeof(clientAddr);

	std::list<SOCKET> clientSocks;
	while (1)
	{
		std::cout << "running" << std::endl;
	
		FD_ZERO(&fdread);
		FD_SET(listenSock, &fdread);
		SOCKET connSock = accept(listenSock, (sockaddr*)&clientAddr, &clientAddrLen);
		if (INVALID_SOCKET == connSock)
		{
			iret = WSAGetLastError();
			if (WSAEWOULDBLOCK == iret)
			{
				std::cout << "waitting ..." << std::endl;
			}
			else
			{
				closesocket(listenSock);
				break;
			}
		}
		else
		{
			clientSocks.push_back(connSock);
		}

		std::list<SOCKET>::iterator iter = clientSocks.begin();
		for ( ; iter!=clientSocks.end(); ++iter)
		{
			FD_SET(*iter, &fdread);
		}

		if (SOCKET_ERROR == select(0, &fdread, NULL, NULL, NULL))
		{
			std::cout << "select failed, errcode:" << WSAGetLastError() << std::endl;
			continue;
		}

		handleConn(clientSocks, fdread);
	}

	system("pause");
	return 0;
}

void handleConn(std::list<SOCKET> &clientSocks, fd_set &fdread)
{
	std::cout << "handle connection" << std::endl; 
	std::list<SOCKET>::iterator iter = clientSocks.begin();
	while (iter != clientSocks.end())
	{
		bool isError = false;
		if (FD_ISSET(*iter, &fdread))
		{
			char buf[1000];
			int irecv = recv(*iter, buf, sizeof(buf), 0);
			int lastError = 0;
			if (SOCKET_ERROR == irecv || 0 == irecv)
			{
				lastError = WSAGetLastError();
				std::cout << "socket:" << *iter << ", erase, lasterror:" << lastError << std::endl;
				isError = true;
			}
			else
			{
				int isend = send(*iter, buf, irecv, 0);
				if (isend < 0)
				{
					lastError = WSAGetLastError();
					if (WSAEWOULDBLOCK == lastError)
					{
						::Sleep(100);
					}
					else
					{
						isError = true;
						std::cout << "echo error, lasterror:" << lastError << std::endl;
					}
				}
			}
		}
		if (isError)
		{
			closesocket(*iter);
			clientSocks.erase(iter++);
		}
		else
		{
			++iter;
		}
	}
}