4.1.1 阻塞模式

来源:互联网 发布:中航软件 编辑:程序博客网 时间:2024/05/16 03:59

  Windows套接字在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。

在阻塞模式的套接字上,调用任何一个Windows Sockets API都会耗费不确定的等待时间。图所示,在调用recv()函数时,发生在内核中等待数据和复制数据的过程。

当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。

Windows套接字程序使用“生产者-消费者”模式来解决上述问题。在程序中,“生产者”读入数据,“消费者”根据需求对读入数据进行处理。通常“生产者”和“消费者”存在于两个线程中,当“生产者”完成读入数据时,使用线程同步机制,例如设置一个事件通知“消费者”,“消费者”接收到这个事件后对读入的数据进行处理。

大多数

在下面的程序清单中,ReadDataThread线程用于读入数据,HandleDataThread线程用于对数据进行处理。在ReadDataThread线程中,当数据全部读入后,调用SetEvent()函数设置事件为有信号状态。此时HandleDataThread线程在WaitForSingleObject()函数处处于等待状态,当它收到事件通知后该函数返回,然后对数据进行处理。

该片段程序清单如下。

#define                        NUM_REQUIRED              1024                   //需要读入数据的大小

#define                        MAX_SIZE                            2048                   //输入缓冲区的大小

HANDLE                     hEvent;                                                    //事件句柄

TCHAR                        buff[MAX_SIZE];                                     //输入缓冲区

bool                             done;                                                       //是否退出线程

SOCKET                     sock;                                                       //Windows socket

 

void  ReadDataThread (void)

{

 

         int nTotal = 0;                                                                     //已经读入缓冲区字节数

         int nRead = 0;                                                                    //在调用recv时实际读入字节数

         int nLeft = 0;                                                                       //剩下数据的字节数

         int nBytes = 0;                                                                    //当前已读数据在缓冲区的位置

 

         while (!done)                                                                      //是否退出读线程

         {

                   nTotal = 0;

                   nLeft = NUM_REQUIRED;

                   while (nTotal != NUM_REQUIRED)                     //已经读入缓冲区的字节数不等于需要读入的大小时

                   {

                            nRead = recv(sock, &buff[MAX_SIZE - nBytes],nLeft,0);//接收数据

                            if(SOCKET_ERROR == nRead)                  //读操作失败

                            {

                                     cout<<"error"<<endl;

                                     return;

                            }

                           

                            nTotal += nRead;

                            nLeft -= nRead;

                            nBytes += nRead;

                           

                   }

                  

                   SetEvent(hEvent);                                                    //设置事件为有信号状态

         }

}

 

void HandleDataThread (void)

{

         while (!done)

         {

                   WaitForSingleObject(hEvent,INFINITE);             //等待事件

                  

                   HandleData(buff);                                                    //处理数据

         }       

}

“生产者-消费者”模式并不能改变阻塞套接字在调用Windows Socket API函数时对线程的阻塞状态。但是,该模式使得应用程序可以在阻塞线程之外的线程做其他的事情。

原创粉丝点击