异步SOCKET与同步SOCKET

潘兴朝
2023-12-01

阻塞与非阻塞SOCKET
Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。而在非阻塞模式下,Winsock函数无论如何都会立即返回。

Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。

在同步模式中,对执行网络操作的函数(如Send和receive)的调用一直等到操作完成后才将控制权返回给调用程序;

在异步模式中,这些调用立即返回控制权

阻塞模式

当使用socket()函数创建套接字时,默认的套接字都是阻塞的。这意味着当调用Windows Sockets API不能立即完成时,线程处于等待状态,直到操作完成。


1.输入操作

recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。

2.输出操作

send()、sendto()、WSASend()和WSASendto()函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。

3.接受连接

accept()和WSAAcept()函数。以阻塞套接字为参数调用该函数,等待接受对方的连接请求。如果此时没有连接请求,线程就会进入睡眠状态。

4.外出连接

connect()和WSAConnect()函数。对于TCP连接,客户端以阻塞套接字为参数,调用该函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。


使用阻塞模式的套接字,开发网络程序比较简单,容易实现。当希望能够立即发送和接收数据,且处理的套接字数量比较少的情况下,使用阻塞模式来开发网络程序比较合适。

非阻塞模式
     把套接字设置为非阻塞模式,即通知系统内核:在调用Windows Sockets API时,不要让线程睡眠,而应该让函数立即返回。

当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的。在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式。还可以使用WSAAsyncselect()和WSAEventselect()函数。当调用该函数时,套接字会自动地设置为非阻塞方式。

套接字设置为非阻塞模式后,在调用Windows Sockets API函数时,调用函数会立即返回。大多数情况下,这些函数调用都会调用“失败”,并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常,应用程序需要重复调用该函数,直到获得成功返回代码。(bind, WSAStartup  不会返回该错误)


由于使用非阻塞套接字在调用函数时,会经常返回WSAEWOULDBLOCK错误。所以在任何时候,都应仔细检查返回代码并作好对“失败”的准备。应用程序连续不断地调用这个函数,直到它返回成功指示为止。上面的程序清单中,在While循环体内不断地调用recv()函数,以读入1024个字节的数据。这种做法很浪费系统资源。

要完成这样的操作,有人使用MSG_PEEK标志调用recv()函数查看缓冲区中是否有数据可读。同样,这种方法也不好。因为该做法对系统造成的开销是很大的,并且应用程序至少要调用recv()函数两次,才能实际地读入数据。较好的做法是,使用套接字的“I/O模型”来判断非阻塞套接字是否可读可写。

非阻塞模式套接字 vs 阻塞模式套接字
1. 使用非阻塞模式套接字,需要编写更多的代码,以便在每个Windows Sockets API函数调用中,对收到WSAEWOULDBLOCK错误进行处理。因此,非阻塞套接字便显得有些难于使用。

2. 非阻塞套接字在控制建立的多个连接,在数据的收发量不均,时间不定时,明显具有优势。

通常情况下,可考虑使用套接字的“I/O模型”,它有助于应用程序通过异步方式,同时对一个或多个套接字的通信加以管理。

MFC CAsynSocket
MFC 定义了两个套接字类:CAsyncSocket、CSocket;CSocket派生于CAsyncSocket;
CAsyncSocket类在低层次上对 Windows Sockets API 进行了封装,其成员函数和 Windows Sockets API 函数直接相对应 。

一个CAsyncSocket对 象 就 代 表 了一 个 套 接 字。

而CSocket继承于CAsyncSocket 类,是对 Windows Sockets API 的高级封装。

一 客户端程序:

1. 创建一个Dialog Based项目:CSockClient

2. 设计对话框

去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中为CCSockClientDlg类添加变量。

IDC_EDITMSG CEdit m_MSG
IDC_LISTMSG ClistBox m_MSGS

 3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。

由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:

以Public方式继承CAsyncSocket类,生成新类MySock;

为MySock类添加虚函数OnReceive、OnConnect、OnSend

4. 在MySock.h中添加以下代码


public:
      BOOL m_bConnected;    //是否连接
      UINT m_nLength;        //消息长度
      char m_szBuffer[4096];    //消息缓冲区

5.  在MySock.ccp中添加以下代码


原文:https://blog.csdn.net/baidu_19340981/article/details/39317125 
 

 类似资料: