socket通信之六:Overlapped I/O 事件通知模型实现的客户/服务器模型

1.基于事件通知模型的Overlapped I/O(重叠IO模型)


概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。


      需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):

  1. 事件对象通知(event object notification)
  2. 完成例程(completion routines) ,注意,这里并不是完成端口


这一篇实现基于事件对象通知的重叠I/O模型。


既然是基于事件通知,就要求将Windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),发送接收数据的函数参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与Windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据了。


socket连接,WSAOVERLAPPED重叠IO结构,事件对象之间的关系如下图:





可以发现每建立一个socket连接时需要至少创建一个WSAOVERLAPPED结构和它关联,而每个WSAOVERLAPPED需要关联一个WSAEVENT类型的事件对象。所以为了实现多个客户端和服务器端通信,我们也需要像上一篇select模型一样建立一个类来管理多个socket和它们对应的WSAOVERLAPPED结构,WSAEVENT结构。


2.基本的函数和数据结构


2.1.  WSAOVERLAPPED结构


这个结构自然是重叠模型里的核心,它是这么定义的

typedef struct _WSAOVERLAPPED {

  DWORD Internal;

  DWORD InternalHigh;

  DWORD Offset;

  DWORD OffsetHigh;

  WSAEVENT hEvent;      // 唯一需要关注的参数,用来关联WSAEvent对象
       } WSAOVERLAPPED, *LPWSAOVERLAPPED;


我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:


WSAEVENT event;                   // 定义事件
WSAOVERLAPPED AcceptOverlapped ; // 定义重叠结构
event = WSACreateEvent();         // 建立一个事件对象句柄
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重叠结构
AcceptOverlapped.hEvent = event;    

2.2.WSARecv系列函数


在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用到重叠结构,它是这样定义的:

        int WSARecv(
                        SOCKET s,                      // 当然是投递这个操作的套接字
                        LPWSABUF lpBuffers,          // 接收缓冲区,与Recv函数不同,这里需要一个由WSABUF结构构成的数组
                        DWORD dwBufferCount,        // 数组中WSABUF结构的数量
                        LPDWORD lpNumberOfBytesRecvd,  // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
                        LPDWORD lpFlags,             // 设置为0即可
                        LPWSAOVERLAPPED lpOverlapped,  // “绑定”的重叠结构
                        LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了你的问题。对于使用C++ Windows API监视串口中的数据,可以使用异步I/O功能。下面是一个示例代码: ``` #include <windows.h> #include <iostream> #define MAX_DATA_LENGTH 255 void ReadDataFromSerialPort(HANDLE hSerialPort); int main() { HANDLE hSerialPort; DCB dcbSerialParams = { 0 }; // 打开串口 hSerialPort = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr); if (hSerialPort == INVALID_HANDLE_VALUE) { std::cerr << "Error: Unable to open serial port!" << std::endl; return 1; } // 配置串口 dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerialPort, &dcbSerialParams)) { std::cerr << "Error: Unable to get serial port state!" << std::endl; CloseHandle(hSerialPort); return 1; } dcbSerialParams.BaudRate = CBR_9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; if (!SetCommState(hSerialPort, &dcbSerialParams)) { std::cerr << "Error: Unable to set serial port state!" << std::endl; CloseHandle(hSerialPort); return 1; } // 监视串口中的数据 ReadDataFromSerialPort(hSerialPort); // 关闭串口 CloseHandle(hSerialPort); return 0; } void ReadDataFromSerialPort(HANDLE hSerialPort) { OVERLAPPED ov; DWORD bytesRead; char data[MAX_DATA_LENGTH]; ZeroMemory(&ov, sizeof(ov)); ov.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); while (true) { if (!ReadFile(hSerialPort, data, MAX_DATA_LENGTH, &bytesRead, &ov)) { if (GetLastError() == ERROR_IO_PENDING) { // 等待I/O操作完成 WaitForSingleObject(ov.hEvent, INFINITE); // 获取I/O操作结果 if (!GetOverlappedResult(hSerialPort, &ov, &bytesRead, FALSE)) { std::cerr << "Error: Unable to get overlapped result!" << std::endl; break; } // 处理串口中的数据 std::cout << "Data received: " << data << std::endl; } else { std::cerr << "Error: Unable to read data from serial port!" << std::endl; break; } } else { // 处理串口中的数据 std::cout << "Data received: " << data << std::endl; } } CloseHandle(ov.hEvent); } ``` 这段代码会打开COM1串口,并监视串口中的数据,如果有数据到达,就会将数据输出到控制台。你可以根据需要修改COM口号和串口参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值