在网络通讯中,由于网络拥挤或一次发送的数据量过大等原因,经常会发生交换的数据在短时间内不能传送完,收发数据的函数因此不能返回,这种现象叫做阻塞。 Winsock对有可能阻塞的函数提供了两种处理方式:阻塞和非阻塞方式。

阻塞模式

在阻塞方式下,收发数据的函数在被调用后一直要到传送完毕或者出错才能返回。在阻塞期间,被阻的函数不会断调用系统函数GetMessage()来保持消息循环的正常进行。

非阻塞模式
        将一个套接字置为非阻塞模式之后, Winsock API调用会立即返回。一般这些调用都会“失败”,并返回一个WSAEWOULDBLOCK。表明其操作在调用期间没有时间完成。如在系统的输入缓冲区中,并不存在等待的数据,那recv调用就会返回WSAEWOULDBLOCK错误。通常,我们需要重复调用同一个函数,直至获得一个成功返回代码。这不是一个好的方法。通常采用Winsock的套接字I/O模型去处理。

套接字I/O模型共有五种类型,如下:

select(选择) 
  WSAAsyncSelect(异步选择)
  WSAEventSelect(事件选择)
  overlapped(重叠)
  completion port(完成端口)

*WSAAsyncSelect

Winsock通过WSAAsyncSelect()自动地设置套接字处于非阻塞方式。使用WindowsSockets实现Windows网络程序设计的关键就是它提供了对网络事件基于消息的异步存取,用于注册应用程序感兴趣的网络事件。它请求Windows Sockets DLL在检测到套接字上发生的网络事件时,向窗口发送一个消息。

int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);
hWnd:窗口句柄
wMsg:需要发送的消息
lEvent:事件(以下为事件的内容)
值: 含义:
FD_READ 期望在套接字上收到数据(即读准备好)时接到通知
FD_WRITE 期望在套接字上可发送数据(即写准备好)时接到通知
FD_OOB 期望在套接字上有带外数据到达时接到通知
FD_ACCEPT 期望在套接字上有外来连接时接到通知
FD_CONNECT 期望在套接字连接建立完成时接到通知
FD_CLOSE 期望在套接字关闭时接到通知

进行异步选择使用WSAAsyncSelect()函数时,有以下几点需要引起特别的注意:
  .连续使用两次WSAAsyncSelect()函数时,只有第二次设置的事件有效,如:
           WSAAsyncSelect(s,hwnd,wMsg1,FD_READ);
           WSAAsyncSelect(s,hwnd,wMsg2,FD_CLOSE);
        这样只有当FD_CLOSE事件发生时才会发送wMsg2消息。
  .可以在设置过异步选择后通过再次调用WSAAsyncSelect(s,hwnd,0,0);的形式取消在套接字上所设置的异步事件。
  .Windows Sockets DLL在一个网络事件发生后,通常只会给相应的应用程序发送一个消息,而不能发送多个消息。但通过使用一些函数隐式地允许重发此事件的消息,这样就可能再次接收到相应的消息。
  .在调用过closesocket()函数关闭套接字之后不会再发生FD_CLOSE事件。

对UDP协议,这些网络事件主要为:
      FD_READ   期望在套接字收到数据(即读准备好)时接收通知;
      FD_WRITE 期望在套接字可发送数(即写准备好)时接收通知;
    FD_CLOSE 期望在套接字关闭时接电通知
  消息变量wParam指示发生网络事件的套接字,变量1Param的低字节描述发生的网络事件,高字包含错误码。如在窗口函数的消息循环中均加一个分支:
int ok=sizeof(SOCKADDR);
case wMsg;
switch(1Param)
{
    case FD_READ:  //套接字上读数据 
    if(recvfrom(sr.lpPlayData[j],dwDataSize,0,(struct sockaddr FAR*)&there1,
     (int FAR*)&ok)==SOCKET_ERROR0) {
                MessageBox(hwnd,“数据接收失败!”,“”,MB_OK);
                return(FALSE);
       }
    case FD_WRITE:    //套接字上写数据
  }
break;

*WSAEventSelect
      事件通知模型要求在程序中针对使用的每个套接字创建一个事件对象,然后通过事件模式通知程序其套接字是否收到或发送的信息。一般来说这种模式,一般就是通过类似调用waitformultipleObject一样在一个线程中等待信号事件来,来了就处理。具体调用的函数如下:

创建WSACreateEvent函数.该函数的返回值是一个创建好的事件对象句柄。事件对象句柄完后,接下来将其与某个套接字关联在一起,同时注册自己感兴趣的网络事件类型,方法是调用WSAEventSelect函数,对它的定义如下:

int WSAEventSelect (
              SOCKET s,                              //需要非阻塞处理的套接字
              WSAEVENT hEventObject,    //WSACreateEvent 创建来的,关联到socket
              long lNetworkEvents     
               );
    lNetworkEvents,对应一个“位掩码”,用于指定应用程序感兴趣的各种网络事件类型的一个组合。要想获知对这些事件类型的详细说明,请参考早先讨论过的WSAAsyncSelect I/O模型。
      为WSAEventSelect创建的事件拥有两种工作状态,以及两种工作模式。
    两种工作状态分别是“已传信”(signaled)和 “未传信”(nonsignaled)。
    工作模式则包括“人工”(manual reset)和“自动”(auto reset)。

WSACreateEvent缺省时其信号状态为0,且为人工设置,当网络事件触发了与一个套接字关联在一起的事件对象,其事件信号置1。在完成了一个I/O请求的处理之后,需要调用WSAResetEvent复位处理(置信号为0)。
     一个套接字同一个事件对象句柄关联在一起后,应用程序便可开始I/O处理;方法是等待网络事件触发事件对象句柄的工作状态。
     一般而言,在等待网络传来事件时,类似WaitforMultipleObject,其WSAWaitForMultipleEvents函数的设计宗旨便是用来等待一个或多个事件对象句柄,并在事先指定的一个或所有句柄进入有信号状态后,或在超过了一个规定的时间周期后,立即返回(线程往往在这里死等)。

下面是 WSAWaitForMultipleEvents函数的定义:
DWORD WSAWaitForMultipleEvents(
  DWORD cEvents,                 
  const WSAEVENT FAR *lphEvents
  BOOL fWaitAll,                 
  DWORD dwTimeOUT,               
  BOOL fAlertable                
);

其用法和WaitForMultipleObject类似。
cEvents和lphEvents参数定义了由WSAEVENT对象构成的一个数组。在这个数组中,cEvents指定的是事件对象的数量,而lphEvents对应的是一个指针,用于直接引用该数组。
     要注意的是, WSAWaitForMultipleEvents只能支持由WSA_MAXIMUM_WAIT_EVENTS对象规定的一个最大值,在此定义成64个。故该I/O模型一次最多都只能支持64个套接字。假如想让这个模型同时管理不止64个套接字,必须创建更多的工作者线程,以便等待更多的事件对象。

fWaitAl l 参数指定了WSAWaitForMultiple Events如何等待在事件数组中的对象。
   =TRUE,那么只有等lphEvents数组内包含的所有事件对象都处于有信号状态,函数才会返回;

=FALSE,任一个事件对象进入有信号时,函数就会返回。

dwTimeout参数规定了 WSAWaitForMultipleEvents最多可等待一个网络事件发生有多长时间。超过规定的时间,函数就会立即返回。并返回WSA_WAIT_TIMEOUT。如dwsTimeout设为WSA_INFIN ITE(永远等待),那么根据fWaiiAll或等待一个网络事件或所有网络事件都传信号后,才能从该函数退出。
 fAlertable,缺省设为FALSE。主要用于在重叠式I/O模型中.

当设置fWaiAll=false,WaitForMultipleObject再有网络事件时,会返回一个值,指出造成函数返回的事件对象。根据WSAWaitForMultipleEvents的返回值,减去预定义值WSA_WAIT_EVENT_0,得到具体的引用值(即索引位置),程序便可用事件数组中已发信号的事件,检索与那个事件对应的套接字,知道了造成网络事件的套接字后,调用 WSAEnumNetworkEvents函数,调查发生了什么类型的网络事件。该函数定义如下:
int WSAEnumNetworkEvents (
  SOCKET s,                                      //检索该套接字
  WSAEVENT hEventObject,             
  LPWSANETWORKEVENTS lpNetworkEvents 
);
    hEventObject参数则是可选的;它指定了一个事件句柄,对应于打算重设的那个事件对象。由于我们的事件对象处在一个有信号状态,所以可将它传入,令其自动成为无信号状态。
    也可以采用使用 WSAResetEvent 函数复位事件信号。
   lpNetworkEvents,就是返回的结果信息,它是一个指向WSANETWORKEVENTS结构的指针,用于接收套接字上发生的网络事件类型以及可能出现的任何错误代码。

其WSANETWORKEVENTS结构的定义:
typedef struct _WSANETWORKEVENTS
{
     long lNetworkEvents;
     int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
lNetworkEvents参数指定了一个值,对应于套接字上发生的所有网络事件类型。
      注意一个事件进入置1(有信号)状态时,可能会同时发生多个网络事件类型。如,一个忙的服务器可能同时收到FD_READ和FD_WRITE通知。 iErrorCode参数指定的是一个错误代码数组,同lNetworkEvents中的事件关联在一起。针对每个网络事件类型,都存在着一个特殊的事件索引,名字与事件类型的名字类似,只是要在事件名字后面添加一个“ _BIT”后缀字串即可。如,对FD_READ事件类型来说,iErrorCode数组的索引标识符便是FD_READ_BIT。

c++Socket 异步通讯的更多相关文章

  1. .net平台下socket异步通讯(代码实例)

    你应该知道的.net平台下socket异步通讯(代码实例) 1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上 ...

  2. 你应该知道的.net平台下socket异步通讯(代码实例)

    1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上添加TextBox控件 using System.Net; ...

  3. C#上位机之—WinForm实现Socket异步通讯示例

    工作中常用到的一些知识点,总是用完就忘,第一次尝试用博客记录下来,以备后用: Socket通讯,Socket(套接字)是基于TCP/IP通讯方式的封装好的类,调用时需要添加下面的服务引用: using ...

  4. Socket异步通讯

    1.可以通过多线程来解决(一会补上) 2.Socket在tcp/udp两种通信协议下的异步通信: 基于TCP的异步通信: BeginAccept方法和endeaccept方法 包含在System.Ne ...

  5. .net平台下socket异步通讯

    1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上添加TextBox控件 using System.Net; ...

  6. C# socket异步通讯

    Server: using System; using System.Net; using System.Net.Sockets; using System.Text; namespace TCP_S ...

  7. Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯

    Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...

  8. socket异步编程--libevent的使用

    使用 libevent 和 libev 提高网络应用性能 http://www.ibm.com/developerworks/cn/aix/library/au-libev/ libevent实现ht ...

  9. Socket异步发送的同步控制

    在网络通信中,我们使用Socket异步发送数据,但在客户端,往往是需要等待服务器的返回结果后(握手过程)再往下执行,这就涉及到同步控制了,在多次的实现中,使用AutoResetEvent,实现不,即有 ...

随机推荐

  1. DNS负载均衡

    1)DNS负载均衡的介绍 对于负载均衡的一个典型应用就是DNS负载均衡.庞大的网络地址和网络域名绝对是负载均衡体现优势的地方.那么它的具体原理是如何的呢?本文就将为大家详细介绍一下相关内容. DNS负 ...

  2. java JNI 的实现(2)-java和C/C++的相互调用.

    目录 概述 一,java代码 二,稍微注意通过javah生成的'C/C++'.h头文件和源java代码的关系 三,在C/C++中实现java的native方法(完整C/C++) 1,修改age,即Ja ...

  3. php改写session到数据库

    session改写mysql 在调用 session_start();的地方改用实例化本类即可new SessionDB(); session_set_save_handler( array($thi ...

  4. WindowManager

    我们Android平台是一个又一个的Activity组成的,每一个Activity有一个或者多个View构成.所以说,当我们想显示一个界面的时候,我们首先想到的是建立一个Activity,然后所有的操 ...

  5. poj1182 并查集

     题目连接:http://poj.org/problem?id=1182 基础并查集,需要维护与根节点关系,解析见代码: /* poj 1182 并查集 思路分析:让你分析这些话里面多少假的 只需要用 ...

  6. QT5-控件-QTimeEdit和QTime

    #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTimeEdit> #i ...

  7. 快速幂:quickpow

    众所周知,快速幂是优化对数的次方运算的最普遍手段.在学习快速幂的思想时,其分治思想容易让大家用简单的递归实现. 但其实,除了递归之外,更好的方法会是简单的 WHILE循环.下面贴代码: #includ ...

  8. cookie,localStorage,sessionStorage

    1.cookie 由于HTTP是一种无状态的协议,服务器单从请求内容上无法判断客户身份.因此,cookie弥补这个缺陷,每次请求都携带cookie,这样服务器就能能知道不同的客户端请求了. 对于同一域 ...

  9. mysql 获取当前日期及格式化

    MYSQL 获取当前日期及日期格式获取系统日期: NOW()格式化日期: DATE_FORMAT(date, format)注: date:时间字段format:日期格式 返回系统日期,输出 2009 ...

  10. C语言+ODBC+SQL 操作(向SQL里面添加数据)

    为了节省时间,我就引用上一节的数据库的表和C语言的结构体数组,在结构体数组中添加数据,清空数据库数据. 第一步查询:SQLBindParameter函数的用法. SQLRETURN SQLBindPa ...