WSAEventSelect模型例子(使用线程池)

以下来自《Windows 网络与通信程序设计(第2版)》3.4.3
1.
WinDef.h中的声明:

#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif


2.
WinNT.h中的声明:
typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION , *PRTL_CRITICAL_SECTION;

WinBase.h中的声明:
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;  //关键区(段)

3.
MSDN中关于CloseHandle的说明:
The CloseHandle function closes an open object handle.

BOOL CloseHandle (
  HANDLE hObject
);

Parameters
hObject
[in] Handle to an open object. This parameter can be a pseudo handle or INVALID_HANDLE_VALUE.
Return Values
If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

If the application is running under a debugger, the function will throw an exception if it receives either a handle value that is not valid or a pseudo-handle value. This can happen if you close a handle twice, or if you call CloseHandle on a handle returned by the FindFirstFile function.

Remarks
The CloseHandle function closes handles to the following objects:

Access token
Communications device
Console input
Console screen buffer
Event
File
File mapping
Job
Mailslot
Memory resource notification
Mutex
Named pipe
Pipe
Process
Semaphore
Socket
Thread
Waitable timer


CloseHandle invalidates the specified object handle, decrements the object's handle count, and performs object retention checks. After the last handle to an object is closed, the object is removed from the system.

Closing a thread handle does not terminate the associated thread. To remove a thread object, you must terminate the thread, then close all handles to the thread.

Use CloseHandle to close handles returned by calls to the CreateFile function. Use FindClose to close handles returned by calls to FindFirstFile.

4.
MSDN中关于closesocket的说明:
The closesocket function closes an existing socket.

int closesocket (
  SOCKET s
);

Parameters
s
[in] Descriptor identifying the socket to close.
Return Values
If no error occurs, closesocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

5.
MSDN中关于GlobalFree的说明:
The GlobalFree function frees the specified global memory object(释放指定的全局内存对象) and invalidates its handle.(使其句柄无效)

Note  The global functions are slower than other memory management functions and do not provide as many features. Therefore, new applications should use the heap functions. However, the global functions are still used with DDE and the clipboard functions.

HGLOBAL GlobalFree (
  HGLOBAL hMem
);

Parameters
hMem
[in] Handle to the global memory object. This handle is returned by either the GlobalAlloc or GlobalReAlloc function. It is not safe to free memory allocated with LocalAlloc.
Return Values
If the function succeeds, the return value is NULL.

If the function fails, the return value is equal to a handle to the global memory object. To get extended error information, call GetLastError.

6.
MSDN中关于InitializeCriticalSection的说明:
The InitializeCriticalSection function initializes a critical section object.

void InitializeCriticalSection (
  LPCRITICAL_SECTION lpCriticalSection
);

Parameters
lpCriticalSection
[out] Pointer to the critical section object.
Return Values
This function does not return a value.
In low memory situations, InitializeCriticalSection can raise a STATUS_NO_MEMORY exception.(在低内存情况下,可能会引发异常)

7.
MSDN中关于EnterCriticalSection的说明:
The EnterCriticalSection function waits for ownership of the specified critical section object.(等待指定关键区对象的所有权) The function returns when the calling thread is granted ownership.(当调用线程被赋予了所有权时,该函数返回)

void EnterCriticalSection (
  LPCRITICAL_SECTION lpCriticalSection
);

Parameters
lpCriticalSection
[in, out] Pointer to the critical section object.
Return Values
This function does not return a value.

This function can raise EXCEPTION_POSSIBLE_DEADLOCK if the critical section is corrupt or deadlock detection is enabled. Do not handle this exception; either continue execution or debug the application.

Windows 2000/NT:  In low memory situations, EnterCriticalSection can raise an exception. To avoid problems, use structured exception handling, or call the InitializeCriticalSectionAndSpinCount function to preallocate the event used by EnterCriticalSection instead of calling the InitializeCriticalSection function, which forces EnterCriticalSection to allocate the event.

8.
MSDN中关于LeaveCriticalSection的说明:
The LeaveCriticalSection function releases ownership of the specified critical section object.(释放指定关键区对象的所有权)

void LeaveCriticalSection (
  LPCRITICAL_SECTION lpCriticalSection
);

Parameters
lpCriticalSection
[in, out] Pointer to the critical section object.
Return Values
This function does not return a value.

9.
MSDN中关于DeleteCriticalSection的说明:
The DeleteCriticalSection function releases all resources used by an unowned critical section object.(释放一个unowned关键区对象使用或占用的所有资源)

void DeleteCriticalSection (
  LPCRITICAL_SECTION lpCriticalSection
);

Parameters
lpCriticalSection
[in, out] Pointer to the critical section object. The object must have been previously initialized with the InitializeCriticalSection function.
Return Values
This function does not return a value.

10.
MSDN中关于InterLockedIncrement的说明:
The InterlockedIncrement function increments (increases by one) the value of the specified 32-bit variable(对指定的32位变量的值加1) and checks the resulting value. The function prevents more than one thread from using the same variable simultaneously.(该函数阻止多于1个的线程同时使用这个相同的变量)

To operate on 64-bit values, use the InterlockedIncrement64 function.

LONG InterlockedIncrement (
  LONG volatile* Addend
);

Parameters
Addend
[in, out] Pointer to the variable to be incremented.
Return Values
The function returns the resulting incremented value.
Windows NT 3.51 and earlier and Windows 95:  If the result of the operation is zero, the return value is zero. If the result of the operation is less than zero, the return value is negative, but it is not necessarily equal to the result. If the result of the operation is greater than zero, the return value is positive, but it is not necessarily equal to the result.

11.
MSDN中关于InterLockedDecrement的说明:(类似上面)
The InterlockedDecrement function decrements (decreases by one) the value of the specified 32-bit variable and checks the resulting value. The function prevents more than one thread from using the same variable simultaneously.

To operate on 64-bit values, use the InterlockedDecrement64 function.

LONG InterlockedDecrement (
  LONG volatile* Addend
);

Parameters
Addend
[in, out] Pointer to the variable to be decremented.
Return Values
The function returns the resulting decremented value.
Windows NT 3.51 and earlier and Windows 95:  If the result of the operation is zero, the return value is zero. If the result of the operation is less than zero, the return value is negative, but it is not necessarily equal to the result. If the result of the operation is greater than zero, the return value is positive, but it is not necessarily equal to the result.

12.
MSDN中关于WSASetEvent的说明:
The WSASetEvent function sets the state of the specified event object to signaled.(该函数设置指定事件对象的状态为有信号(通知))

BOOL WSASetEvent (
  WSAEVENT hEvent
);

Parameters
hEvent
[in] Handle that identifies an open event object.
Return Values
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error information, call WSAGetLastError.

13.
MSDN中关于ResetEvent的说明:
The ResetEvent function sets the specified event object to the nonsignaled state.(设置指定事件对象的状态为无信号状态)

BOOL ResetEvent (
  HANDLE hEvent
);

Parameters
hEvent
[in] Handle to the event object. The CreateEvent or OpenEvent function returns this handle.
The handle must have the EVENT_MODIFY_STATE access right. For more information, see Synchronization Object Security and Access Rights.

Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

14.
比较查看:
WaitForSingleObject
WaitForSingleObjectEx
WaitForMultipleObjects.



15.
MSDN中关于CreateThread的说明:
The CreateThread function creates a thread to execute within the virtual address space of the calling process.
(该函数创建一个线程,让其在调用进程的虚拟地址空间中执行)
To create a thread that runs in the virtual address space of another process, use the CreateRemoteThread function.

HANDLE CreateThread (
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  SIZE_T dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId
);

Parameters(参数说明还没有细细的看)
lpThreadAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpThreadAttributes is NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor for the new thread. If lpThreadAttributes is NULL, the thread gets a default security descriptor. The ACLs in the default security descriptor for a thread come from the primary token of the creator.

Windows XP/2000/NT:  The ACLs in the default security descriptor for a thread come from the primary or impersonation token of the creator. This behavior changed with Windows XP SP2 and Windows Server 2003.

dwStackSize
[in] Initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is zero, the new thread uses the default size for the executable. For more information, see Thread Stack Size.
lpStartAddress
[in] Pointer to the application-defined function to be executed by the thread and represents the starting address of the thread. For more information on the thread function, see ThreadProc.
lpParameter
[in] Pointer to a variable to be passed to the thread.
dwCreationFlags
[in] Flags that control the creation of the thread. If the CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and will not run until the ResumeThread function is called. If this value is zero, the thread runs immediately after creation.
If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified, the dwStackSize parameter specifies the initial reserve size of the stack. Otherwise, dwStackSize specifies the commit size.

Windows 2000/NT and Windows Me/98/95:  The STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported.
lpThreadId
[out] Pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
Windows Me/98/95:  This parameter may not be NULL.

下面是一个基于WSAEventSelect模型的服务器程序:
不同的是使用了线程池,可以处理大量的客户I/O请求;
设计的思路:程序的主线程负责监听客户端的连接请求,接受到新连接之后,将新套接字安排给工作线程处理I/O;每个工作线程最多处理64个套接字连接,如果再有新的套接字,就再创建新的工作线程。

套接字对象结构 ,记录每个客户端套接字的信息:
typedef struct _SOCKET_OBJ
{
    SOCKET s;                    // 套节字句柄
    HANDLE event;            // 与此套节字相关联的事件对象句柄
    sockaddr_in addrRemote;        // 客户端地址信息
    _SOCKET_OBJ *pNext;            // 指向下一个SOCKET_OBJ对象,为的是连成一个表
} SOCKET_OBJ, *PSOCKET_OBJ;
服务器程序每接受一个新的连接,就为新连接申请一个SOCKET_OBJ结构,初始化结构的成员;当连接关闭或者出错时,再释放内存空间。


线程对象结构 ,记录每个线程的信息:
typedef struct _THREAD_OBJ
{
    HANDLE events[WSA_MAXIMUM_WAIT_EVENTS];    // 记录当前线程要等待的事件对象的句柄
    int nSocketCount;            // 记录当前线程处理的套节字的数量 <=  WSA_MAXIMUM_WAIT_EVENTS
    PSOCKET_OBJ pSockHeader;       // 当前线程处理的套节字对象列表,pSockHeader指向表头
    PSOCKET_OBJ pSockTail;             // pSockTail指向表尾
    CRITICAL_SECTION cs;                 // 关键代码段变量,为的是同步对本结构的访问
    _THREAD_OBJ *pNext;                // 指向下一个THREAD_OBJ对象,为的是连成一个表
} THREAD_OBJ, *PTHREAD_OBJ;

套接字对象链表记录在它所属的线程对象内,线程对象链表记录在全局变量中:
PTHREAD_OBJ g_pThreadList;        // 指向线程对象列表表头
CRITICAL_SECTION g_cs;              // 同步对此全局变量的访问


当客户数量增加时,服务器就要创建额外的线程去处理I/O。没创建一个线程,便为新线程申请一个THREAD_OBJ结构,初始化该结构的成员。当客户数量减少,处理I/O的线程关闭时,在释放占用的内存空间。

头文件定义如下:
/
// EventSelectServer.h文件

DWORD WINAPI ServerThread(LPVOID lpParam);


// 套节字对象
typedef struct _SOCKET_OBJ
{
    SOCKET s;                       // 套节字句柄
    HANDLE event;                // 与此套节字相关联的事件对象句柄
    sockaddr_in addrRemote;        // 客户端地址信息

    _SOCKET_OBJ *pNext;            // 指向下一个SOCKET_OBJ对象,为的是连成一个表
} SOCKET_OBJ, *PSOCKET_OBJ;

// 线程对象
typedef struct _THREAD_OBJ
{
    HANDLE events[WSA_MAXIMUM_WAIT_EVENTS];    // 记录当前线程要等待的事件对象的句柄
    int nSocketCount;                        // 记录当前线程处理的套节字的数量 <=  WSA_MAXIMUM_WAIT_EVENTS

    PSOCKET_OBJ pSockHeader;                // 当前线程处理的套节字对象列表,pSockHeader指向表头
    PSOCKET_OBJ pSockTail;                      // pSockTail指向表尾

    CRITICAL_SECTION cs;                        // 关键代码段变量,为的是同步对本结构的访问
    _THREAD_OBJ *pNext;                        // 指向下一个THREAD_OBJ对象,为的是连成一个表

} THREAD_OBJ, *PTHREAD_OBJ;

// 线程列表
PTHREAD_OBJ g_pThreadList;        // 指向线程对象列表表头
CRITICAL_SECTION g_cs;              // 同步对此全局变量的访问


// 状态信息
LONG g_nTatolConnections;        // 总共连接数量
LONG g_nCurrentConnections;        // 当前连接数量


// 申请一个套节字对象,初始化它的成员
PSOCKET_OBJ GetSocketObj(SOCKET s)   
{
    PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
    if(pSocket != NULL)
    {
        pSocket->s = s;
        pSocket->event = ::WSACreateEvent();
    }
    return pSocket;
}

// 释放一个套节字对象
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
    ::CloseHandle(pSocket->event);
    if(pSocket->s != INVALID_SOCKET)
    {
        ::closesocket(pSocket->s);
    }
    ::GlobalFree(pSocket);
}

// 申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中
PTHREAD_OBJ GetThreadObj()
{
    PTHREAD_OBJ pThread = (PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));
    if(pThread != NULL)
    {   
        ::InitializeCriticalSection(&pThread->cs);
        // 创建一个事件对象,用于指示该线程的句柄数组需要重组
        pThread->events[0] = ::WSACreateEvent();

        // 将新申请的线程对象添加到列表中
        ::EnterCriticalSection(&g_cs);
        pThread->pNext = g_pThreadList;   //在线程链表头插入节点
        g_pThreadList = pThread;
        ::LeaveCriticalSection(&g_cs);
    }
    return pThread;
}

// 释放一个线程对象,并将它从线程对象列表中移除
void FreeThreadObj(PTHREAD_OBJ pThread)
{
    // 在线程对象列表中查找pThread所指的对象,如果找到就从中移除
    ::EnterCriticalSection(&g_cs);
    PTHREAD_OBJ p = g_pThreadList;
    if(p == pThread)        // 是第一个?
    {
        g_pThreadList = p->pNext;
    }
    else
    {
        while(p != NULL && p->pNext != pThread)
        {
            p = p->pNext;
        }
        if(p != NULL)
        {
            // 此时,p是pThread的前一个,即“p->pNext == pThread”
            p->pNext = pThread->pNext;
        }
    }
    ::LeaveCriticalSection(&g_cs);

    // 释放资源
    ::CloseHandle(pThread->events[0]);
    ::DeleteCriticalSection(&pThread->cs);
    ::GlobalFree(pThread);
}

// 重新建立线程对象的events数组
void RebuildArray(PTHREAD_OBJ pThread)   
{
    ::EnterCriticalSection(&pThread->cs);
    PSOCKET_OBJ pSocket = pThread->pSockHeader;
    int n = 1;    // 从第1个开始写,第0个用于指示需要重建了
    while(pSocket != NULL)
    {
        pThread->events[n++] = pSocket->event;
        pSocket = pSocket->pNext;
    }
    ::LeaveCriticalSection(&pThread->cs);
}

/

// 向一个线程的套节字列表中插入一个套节字
BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
    BOOL bRet = FALSE;
    ::EnterCriticalSection(&pThread->cs);
    if(pThread->nSocketCount < WSA_MAXIMUM_WAIT_EVENTS - 1)
    {
        if(pThread->pSockHeader == NULL)
        {
            pThread->pSockHeader = pThread->pSockTail = pSocket;
        }
        else
        {
            pThread->pSockTail->pNext = pSocket;  //在套接字对象结构链表尾插入节点
            pThread->pSockTail = pSocket;
        }
        pThread->nSocketCount ++;
        bRet = TRUE;
    }
    ::LeaveCriticalSection(&pThread->cs);

    // 插入成功,说明成功处理了客户的连接请求
    if(bRet)
    {
        ::InterlockedIncrement(&g_nTatolConnections);
        ::InterlockedIncrement(&g_nCurrentConnections);
    }   
    return bRet;
}

// 将一个套节字对象安排给空闲的线程处理
void AssignToFreeThread(PSOCKET_OBJ pSocket)
{   
    pSocket->pNext = NULL;

    ::EnterCriticalSection(&g_cs);
    PTHREAD_OBJ pThread = g_pThreadList;//全局变量系统初始化为0,在这里等于NULL
    // 试图插入到现存线程
    while(pThread != NULL)
    {
        if(InsertSocketObj(pThread, pSocket))
            break;
        pThread = pThread->pNext;
    }

    // 没有空闲线程,为这个套节字创建新的线程
    if(pThread == NULL)
    {
        pThread = GetThreadObj();
        InsertSocketObj(pThread, pSocket);   
        ::CreateThread(NULL, 0, ServerThread, pThread, 0, NULL);
    }
    ::LeaveCriticalSection(&g_cs);

    // 指示线程重建句柄数组
    ::WSASetEvent(pThread->events[0]);  //这个很重要
}

// 从给定线程的套节字对象列表中移除一个套节字对象
void RemoveSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
    ::EnterCriticalSection(&pThread->cs);

    // 在套节字对象列表中查找指定的套节字对象,找到后将之移除
    PSOCKET_OBJ pTest = pThread->pSockHeader;
    if(pTest == pSocket)
    {
        if(pThread->pSockHeader == pThread->pSockTail)
            pThread->pSockTail = pThread->pSockHeader = pTest->pNext;
        else
            pThread->pSockHeader = pTest->pNext;
    }
    else
    {
        while(pTest != NULL && pTest->pNext != pSocket)
            pTest = pTest->pNext;
        if(pTest != NULL)
        {
            if(pThread->pSockTail == pSocket)
                pThread->pSockTail = pTest;
            pTest->pNext = pSocket->pNext;
        }
    }
    pThread->nSocketCount --;

    ::LeaveCriticalSection(&pThread->cs);

    // 指示线程重建句柄数组
    ::WSASetEvent(pThread->events[0]);   //这个很重要

    // 说明一个连接中断
    ::InterlockedDecrement(&g_nCurrentConnections);
}


BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
    // 获取具体发生的网络事件
    WSANETWORKEVENTS event;
    ::WSAEnumNetworkEvents(pSocket->s, pSocket->event, &event);
    do
    {
        if(event.lNetworkEvents & FD_READ)            // 套节字可读
        {
            if(event.iErrorCode[FD_READ_BIT] == 0)
            {
                char szText[256];
                int nRecv = ::recv(pSocket->s, szText, strlen(szText), 0);
                if(nRecv > 0)               
                {
                    szText[nRecv] = '/0';
                    printf("接收到数据:%s /n", szText);
                }
            }
            else
                break;
        }
        else if(event.lNetworkEvents & FD_CLOSE)    // 套节字关闭
        {
            break;
        }
        else if(event.lNetworkEvents & FD_WRITE)    // 套节字可写
        {
            if(event.iErrorCode[FD_WRITE_BIT] == 0)
            {   
            }
            else
                break;
        }
        return TRUE;
    }
    while(FALSE);

    // 套节字关闭,或者有错误发生,程序都会转到这里来执行
    RemoveSocketObj(pThread, pSocket);
    FreeSocketObj(pSocket);
    return FALSE;
}

PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) // nIndex从1开始
{
    // 在套节字列表中查找
    PSOCKET_OBJ pSocket = pThread->pSockHeader;
    while(--nIndex)
    {
        if(pSocket == NULL)
            return NULL;
        pSocket = pSocket->pNext;
    }
    return pSocket;
}

DWORD WINAPI ServerThread(LPVOID lpParam)
{
    // 取得本线程对象的指针
    PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;
    while(TRUE)
    {
        //    等待网络事件
        int nIndex = ::WSAWaitForMultipleEvents(
                            pThread->nSocketCount + 1, pThread->events, FALSE, WSA_INFINITE, FALSE);
        nIndex = nIndex - WSA_WAIT_EVENT_0;
        // 查看受信的事件对象
        for(int i=nIndex; i<pThread->nSocketCount + 1; i++)   //这里的nIndex与上节例子中的作用一样
        {
            nIndex = ::WSAWaitForMultipleEvents(1, &pThread->events[i], TRUE, 1000, FALSE);
            if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
            {
                continue;
            }
            else
            {
                if(i == 0)                // events[0]受信,重建数组
                {
                    RebuildArray(pThread);
                    // 如果没有客户I/O要处理了,则本线程退出
                    if(pThread->nSocketCount == 0)
                    {
                        FreeThreadObj(pThread);
                        return 0;
                    }
                    ::WSAResetEvent(pThread->events[0]);
                }
                else                    // 处理网络事件
                {
                    // 查找对应的套节字对象指针,调用HandleIO处理网络事件
                    PSOCKET_OBJ pSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);
                    if(pSocket != NULL)
                    {
                        if(!HandleIO(pThread, pSocket))
                            RebuildArray(pThread);
                    }
                    else
                        printf(" Unable to find socket object /n ");
                }
            }
        }
    }
    return 0;
}

源文件定义如下:
///
// EventSelectServer.cpp文件

#include "../common/initsock.h"

#include <stdio.h>
#include <windows.h>

#include "EventSelectServer.h"

// 初始化Winsock库
CInitSock theSock;

int main()
{
    USHORT nPort = 4567;    // 此服务器监听的端口号

    // 创建监听套节字
    SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nPort);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf(" Failed bind() /n");
        return -1;
    }
    ::listen(sListen, 200);

    // 创建事件对象,并关联到监听的套节字
    WSAEVENT event = ::WSACreateEvent();
    ::WSAEventSelect(sListen, event, FD_ACCEPT|FD_CLOSE);

    ::InitializeCriticalSection(&g_cs);

    // 处理客户连接请求,打印状态信息
    while(TRUE)
    {
        int nRet = ::WaitForSingleObject(event, 5*1000);
        if(nRet == WAIT_FAILED)
        {
            printf(" Failed WaitForSingleObject() /n");
            break;
        }
        else if(nRet == WSA_WAIT_TIMEOUT)    // 定时显式状态信息
        {
            printf(" /n");
            printf("   TatolConnections: %d /n", g_nTatolConnections);
            printf(" CurrentConnections: %d /n", g_nCurrentConnections);
            continue;
        }
        else                                // 有新的连接未决
        {
            ::ResetEvent(event);
            // 循环处理所有未决的连接请求
            while(TRUE)
            {
                sockaddr_in si;
                int nLen = sizeof(si);
                SOCKET sNew = ::accept(sListen, (sockaddr*)&si, &nLen);
                if(sNew == SOCKET_ERROR)
                    break;
                PSOCKET_OBJ pSocket = GetSocketObj(sNew);
                pSocket->addrRemote = si;
                ::WSAEventSelect(pSocket->s, pSocket->event, FD_READ|FD_CLOSE|FD_WRITE);
                AssignToFreeThread(pSocket);
            }
        }
    }
    ::DeleteCriticalSection(&g_cs);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值