Winsock 套接字模式

阻塞模式

在阻塞模式下,I/O操作完成之前,Winsock调用会一直等待,不会立即返回。

Winsock应用程序通常是遵照“生产者-消费者”模型,应用程序需要读取(或写入)指定数量的字节,然后以该数据为基础执行计算。

SOCKET sock;
char buff [256];
int done = 0;
…
while (!done)
{
    nBytes = recv(sock, buff, 65);
    if (nBytes == SOCKET_ERROR)
    {
        printf(“recv failed with error %d \n”, WSAGetLastError());
        return;
    }
    DoComputationOnData(buff);
}
…

如果输入缓冲区没有数据可读,recv将一直阻塞。可以调用ioctlsocket(设置FIONREAD选项),事先查看缓冲区中是否存在必要的字节数量。不过这将造成系统开销,并不是一种好的编程习惯,应尽力避免。

如果上述代码放在主线程中,则recv可能会阻塞主线程。比较好的办法是创建一个Read线程和一个Process线程,两个线程共享同一数据缓冲区(使用同步对象进行同步)。Read线程负责读入数据并置入缓冲区,当读入Process线程所需的最小数据量之后,触发一个事件,通知Process线程进行处理,处理完之后删除这些数据。

#define MAX_BUFFER_SIZE 4096
// 执行初始化,并在创建两个线程之前,创建一个自动重置的事件(hEvent)
CRITICAL_SECTION data;
HANDLE hEvent;
SOCKET sock;
TCHAR buff[MAX_BUFFER_SIZE];
int done = 0;
// 创建并连接套接字
void ReadThread()
{
    int nTotal = 0, nRead = 0, nLeft = 0, nBytes = 0;
    while (!done)
    {
        nTotal = 0;
        nLeft = NUM_BYTES_REQUIRED;
        while (nTotal != NUM_BYTES_REQUIRED)
        {
            EnterCriticalSection(&data);
            nRead = recv(sock, &(buff[MAX_BUFFER_SIZE – nBytes]), nLeft, 0);
            if (nRead == -1)
            {
                printf(“error \n”);
                ExitThread();
            }
            nTotal += nRead;
            nLeft -= nRead;
            nBytes += nRead;
            LeaveCriticalSection(&data);
        }
        SetEvent(hEvent);
    }
}

void ProcessThread()
{
    WaitForSingleObject(hEvent);
    EnterCriticalSection(&data);
    DoSOmeComputationOnData(buff);
    // 从缓冲区中删除已处理过的数据,并将余下数据移到数组起始处
    nBytes -= NUM_BYTES_REUERED;
    LeaveCriticalSection(&data);
}

非阻塞模式

在非阻塞模式下,Winsock函数无论如何都会立即返回。它在功能上比较强大,但用起来会存在一些难度。下面代码创建一个套接字,并置为非阻塞模式。

SOCKET sock;
unsigned long ul = 1;
int nRet;

sock = socket(AF_INET, SOCK_STREAM, 0);
nRet = ioctlsocket(sock, FIONBIO, (unsigned long *)&ul);
if (nRet == SOCKET_ERROR)
{
    // 未能将套接字置入非阻塞模式
}

在非阻塞模式下,Winsock API会立即返回,但大多数情况下会返回一个WSAEWOULDBLOCK错误,意味着操作在调用期间没有足够时间来完成。通常需要重复调用同一个函数,直到获得成功,但这种做法并没有什么优势可言,所以Winsock的套接字I/O模型可帮助应用程序判断一个套接字何时可供读写。

阻塞套接字容易使用,但在建立多个套接字时,或在数据的收发量不均、时间不定时,极难管理。非阻塞套接字需要编写更多的代码,在任何时候都需仔细检查所有Winsock API的返回代码,对可能的WSAEWOULDBLOCK错误加以处理,所以有些难以操作。在这种情况下,可考虑使用套接字I/O模型,它将帮助应用程序通过一种异步方式,同时对一个或多个套接字上进行的通信加以管理。

【Windows网络编程 – 读书笔记】

posted on 2013-01-28 10:29  zhuyf87  阅读(979)  评论(0编辑  收藏  举报

导航