孙鑫 第十五/十六课之五 基于消息异步套接字编程

来源:互联网 发布:php二次开发 编辑:程序博客网 时间:2024/06/05 18:59

声明

由于接触套接字编程不久,而且异步套接字编程更是刚刚接触,在对照学习完孙鑫老师这节课的视频自己总结的时候有个重大误区,那就是以为孙鑫老师讲的就是近乎全部的异步套接字编程知识,可是没有注意到一个大前提----基于消息的!!!      通过群友和度娘得知:异步套接字有六种I/O模型,my god !    六种有木有!!!   而基于消息的仅仅是其中一种!  其中一种有木有!!!

孙老师的视频里讲WSASocket函数的时候,最后一个参数dwFlags一句话带过,说以后要讲,还有PPT里说的WSARecvFrom和WSASendto中最后两个参数, 差一点就误导了我!    他应该这样做:如果要讲基于消息的套接字编程干脆就用socket函数而别用WSASocket函数,因为WSASocket函数中后三个参数没有使用,其效果和socket函数完全一样,这样再讲基于消息的就不容易迷糊。


如果WSASocket函数中设置了WSA_FLAG_OVERLAPPED重叠操作标识,那么就可用WSASend, WSASendto, WSARecv, WSARecvFrom, WSAIoctl来进行异步重叠操作,当这些函数完成时通过回调函数通知用户。

重叠操作是实现异步操作的一种方式。


说明

所谓异步,就是指不用等函数体的代码执行完毕再返回,而是函数一经调用马上返回继续往下执行。 我们知道在网络编程中,基于TCP的套接字服务器端的accept函数,在客户端连接请求到来之前,服务器端该accept函数所在的线程会阻塞在此处,直到有连接请求到来才能往下执行,如果阻塞的线程多了,那么OS可能会限制新线程的创建,总之这是一种性能不高的编程方式。  而异步方式下,accept函数一经调用马上返回,不会造成所在线程的阻塞,通过注册网络事件的方式,等到有请求到来的时候去执行相应的消息函数处理连接请求。

 

 

相关函数

①WSASocket

SOCKET  WSASocket(

int  af,                  //必须为AF_INET

int  type,              //哪种套接字,SOCK_STREAM 或  SOCK_DGRAM

int  protocol,        //协议,可为0,自动选择合适协议

LPWSAPROTOCOL_INFO   lpProtocolInfo, //WSAPROTOCOL_INFO结构体指针,如果为NULL则按照前三个参数设置,如果不为NULL则忽略前三个参数,按照此参数的设置

GROUP  g,           //套接组口描述符?  保留,为0即可

DWORD  dwFlags //标识

);

函数说明:该函数的第三个参数是一个指向WSAPROTOCOL_INFO结构体的指针,如果为NULL则用前三个参数设置该套接字,如果不为NULL则忽略前三个参数,用此参数设置该套接字。

该函数最后一个参数dwFlags,dwFlags中有一个很重要的标识WSA_FLAG_OVERLAPPED(重叠标识),如果设置了这个标识则该套接字为重叠套接字,可用WSASend, WSASendto, WSARecv, WSARecvFrom, WSAIoctl来进行异步重叠操作。


eg.

SOCKET  sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);   //UDP, 非重叠

 

 

②WSAAsyncSelect

int   WSAAsyncSelect(

SOCKET  s, //套接字

HWND  hWnd, //网络事件发送时消息传递给的窗口句柄,继承自CWnd类的子类都有m_hWnd句柄

unsigned  int   wMsg, //用户自定义消息,其消息处理函数用来对网络事件发生时的处理

long  lEvent //可以注册的网络事件

);

函数说明:该函数可以注册网络事件,当网络事件发生时调用响应的消息处理函数进行处理。

当有错误发生时返回SOCKET_ERROR,可用WSAGetLastError()函数获取错误代码。

可以注册的网络事件有:

FD_READ     FD_WRITE    FD_ACCEPT    FD_CONNECT    FD_CLOSE   FD_OOB    FD_QOS    FD_GROUP_QOS    

FD_ROUTING_INTERFACE_CHANGE      FD_ADDRESS_LIST_CHANGE

 

eg.

if( SOCKET_ERROR == WSAAsyncSelect(sock, m_hWnd, WM_SOCK, FD_READ)) //假设消息WM_SOCK对应的消息处理函数                                                                                                                       // 已经存在, 注册网络读事件

{

    CString str;

    str.Format("%d", WSAGetLastError()); //获得错误代码

    MessageBox(str);

}

 

 

③WSARecvFrom

int   WSARecvFrom(

SOCKET  s,  // [in]

LPWSABUF  lpBuffers, //[in / out]  WSABUF类型结构体指针,可以是一个WSABUF结构体数组,如 WSABUF  wsaBuf[5];  

DWORD  dwBufferCount, //[in] 上一个参数中WSABUF结构体的个数,1个或者多个(数组元素个数)

LPDWORD  lpNumberOfBytesRecvd, //[out] 本次接收到的字节数

LPDWORD  lpFlags, //[in / out]指向标志位的指针

struct  sockaddr FAR*  lpFrom, //对方地址端口结构体

LPINT  lpFromlen, // [int / out] 前一个参数结构体大小,必须初始化

LPWSAOVERLAPPED   lpOverlapped, //指向WSAOVERLAPPED结构体的指针,如果WSASocket中没有设置重叠标识则忽略此参数

LPWSAOVERLAPPED_COMPLETION_ROUTINE         lpCompletionRoutine //指向接收操作完成时调用的完成例程的指针,也就是操作完成时调用的回调函数地址,如果WSASocket中没有设置重叠标识则忽略此参数

);

eg.

//假设SOCKADDR_IN    addrCandidate已存在

WSABUF wsaBuf[5];

DWORD numberOfBytes = 0;

DWORD flags = 0;      //为0即可

int  len = sizeof(SOCKADDR);

WSARecvFrom(sock, wsaBuf, sizeof(wsaBuf) / sizeof(WSABUF), &numberOfBytes, &flags, (SOCKADDR*)&addrCandidate, &len, NULL, NULL);


④WSASendTo

int   WSASendTo(

SOCKET  s,  // [in]

LPWSABUF  lpBuffers, //[in / out]  WSABUF类型结构体指针,可以是一个WSABUF结构体数组,如 WSABUF  wsaBuf[5];  

DWORD  dwBufferCount, //[in] 上一个参数中WSABUF结构体的个数,1个或者多个(数组元素个数)

LPDWORD  lpNumberOfBytesRecvd, //[out] 本次发送的字节数

DWORD  dwFlags, //[in]标志位

const  struct  sockaddr FAR*  lpTo, //发送到的地址端口结构体

int   iTolen, // [int]  前一个参数的长度,一般为SIZEOF(SOCKADDR)

LPWSAOVERLAPPED   lpOverlapped, //指向WSAOVERLAPPED结构体的指针,如果WSASocket中没有设置重叠标识则忽略此参数

LPWSAOVERLAPPED_COMPLETION_ROUTINE         lpCompletionRoutine //指向接收操作完成时调用的完成例程的指针,也就是操作完成时调用的回调函数地址,如果WSASocket中没有设置重叠标识则忽略此参数

);


该函数同WSARecvFrom类似

eg.

//假设SOCKADDR_IN    addrCandidate已存在

WSABUF  wsaBuf[5];

DWORD  numberOfBytes = 0;

DWORD  flags = 0;  //为0即可

WSASentTo(sock, wsaBuf, sizeof(wsaBuf) / sizeof(WASBUF), &numberOfBytes, flags, (SOCKADDR*)&addrCandidate, sizeof(SOCKADDR), NULL, NULL);

 

NOTE: 

WSARecvFrom和WSASendTo中第五个参数的区别:前者是指针类型,后者是DWORD类型。

如果WSASocket函数中没有设置重叠操作标识,那么函数③④可用recvfrom和sendto代替,效果一样。


 

下篇文章讲贴一个基于消息的UDP套接字编程简单实例。

 

 

孙鑫 第十五/十六课之五 基于消息异步套接字编程 - 大灰狼 - 大灰狼 的博客

 

 

原创粉丝点击