select模型

Table of Contents

    需要注意的是要将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;
    		}
    	}
    }