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