IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 网络专题六:windows高性能网络模型IOCP -> 正文阅读

[系统运维]网络专题六:windows高性能网络模型IOCP

/*
总结iocp的几个步骤(注:完成键是在关联iocp的时候传入,重叠体是在投递任务的时候传入)
1.创建空完成端口 createIoCompletionPort()
2.关联完成端口和server套接字createIoCompletionPort()
3.向完成端口投递接收连接任务 accepEx() 完成端口是投递一个任务 处理一个
4.检测完成端口状态getCueuedCompletionStatus()
5.在接收连接完成分支投递接收任务WSARecv()
6.在接收数据完成分支投递发送任务WSASend()
7.在发送完成分支投递接收数据任务WSARecv()
*/

#define WIN32_LEAN_AND_MEAN

#include<windows.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

#include<mswsock.h>
#pragma comment(lib,"Mswsock.lib")

#include<stdio.h>

#define nClient 10
enum IO_TYPE
{
?? ?ACCEPT = 10,
?? ?RECV,
?? ?SEND
};

//数据缓冲区空间大小
#define DATA_BUFF_SIZE 1024
struct IO_DATA_BASE
{
?? ?//重叠体
?? ?OVERLAPPED overlapped;
?? ?//
?? ?SOCKET sockfd;
?? ?//数据缓冲区
?? ?char buffer[DATA_BUFF_SIZE];
?? ?//实际缓冲区数据长度
?? ?int length;
?? ?//操作类型
?? ?IO_TYPE iotype;
};
//向IOCP投递接受连接的任务
void postAccept(SOCKET sockServer, IO_DATA_BASE* pIO_DATA)
{
?? ?pIO_DATA->iotype = IO_TYPE::ACCEPT;
?? ?pIO_DATA->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
?? ?if (FALSE == AcceptEx(sockServer
?? ??? ?, pIO_DATA->sockfd
?? ??? ?, pIO_DATA->buffer
?? ??? ?, 0
?? ??? ?, sizeof(sockaddr_in) + 16
?? ??? ?, sizeof(sockaddr_in) + 16
?? ??? ?, NULL
?? ??? ?, &pIO_DATA->overlapped
?? ?))
?? ?{
?? ??? ?int err = WSAGetLastError();
?? ??? ?if (ERROR_IO_PENDING != err)
?? ??? ?{
?? ??? ??? ?printf("AcceptEx failed with error %d\n", err);
?? ??? ??? ?return;
?? ??? ?}
?? ?}
}
//向IOCP投递接收数据的任务
void postRecv(IO_DATA_BASE* pIO_DATA)
{
?? ?pIO_DATA->iotype = IO_TYPE::RECV;
?? ?WSABUF wsBuff = {};
?? ?wsBuff.buf = pIO_DATA->buffer;//可以指向我们自定义的buffer,方便后续做粘包处理
?? ?wsBuff.len = DATA_BUFF_SIZE;
?? ?DWORD flags = 0;
?? ?ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));

?? ?if (SOCKET_ERROR == WSARecv(pIO_DATA->sockfd, &wsBuff, 1, NULL, &flags, &pIO_DATA->overlapped, NULL))
?? ?{
?? ??? ?int err = WSAGetLastError();
?? ??? ?if (ERROR_IO_PENDING != err)
?? ??? ?{
?? ??? ??? ?printf("WSARecv failed with error %d\n", err);
?? ??? ??? ?return;
?? ??? ?}
?? ?}
}
//向IOCP投递发送数据的任务
void postSend(IO_DATA_BASE* pIO_DATA)
{
?? ?pIO_DATA->iotype = IO_TYPE::SEND;
?? ?WSABUF wsBuff = {};
?? ?wsBuff.buf = pIO_DATA->buffer;
?? ?wsBuff.len = pIO_DATA->length;
?? ?DWORD flags = 0;
?? ?ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));

?? ?if (SOCKET_ERROR == WSASend(pIO_DATA->sockfd, &wsBuff, 1, NULL, flags, &pIO_DATA->overlapped, NULL))
?? ?{
?? ??? ?int err = WSAGetLastError();
?? ??? ?if (ERROR_IO_PENDING != err)
?? ??? ?{
?? ??? ??? ?printf("WSASend failed with error %d\n", err);
?? ??? ??? ?return;
?? ??? ?}
?? ?}
}
//-- 用Socket API建立简易TCP服务端
//-- IOCP Server基础流程
int main()
{
?? ?//启动Windows socket 2.x环境
?? ?WORD ver = MAKEWORD(2, 2);
?? ?WSADATA dat;
?? ?WSAStartup(ver, &dat);
?? ?//------------//
?? ?// 1 建立一个socket
?? ?// 当使用socket函数创建套接字时,会默认设置WSA_FLAG_OVERLAPPED标志
?? ?//
?? ?// 注意这里也可以用 WSASocket函数创建socket
?? ?// 最后一个参数需要设置为重叠标志(WSA_FLAG_OVERLAPPED)
?? ?// SOCKET sockServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
?? ?//
?? ?SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

?? ?// 2.1 设置对外IP与端口信息?
?? ?sockaddr_in _sin = {};
?? ?_sin.sin_family = AF_INET;
?? ?_sin.sin_port = htons(4567);//host to net unsigned short
?? ?_sin.sin_addr.s_addr = INADDR_ANY;
?? ?// 2.2 绑定sockaddr与ServerSocket
?? ?if (SOCKET_ERROR == bind(sockServer, (sockaddr*)&_sin, sizeof(_sin)))
?? ?{
?? ??? ?printf("错误,绑定网络端口失败...\n");
?? ?}
?? ?else {
?? ??? ?printf("绑定网络端口成功...\n");
?? ?}

?? ?// 3 监听ServerSocket
?? ?if (SOCKET_ERROR == listen(sockServer, 64))
?? ?{
?? ??? ?printf("错误,监听网络端口失败...\n");
?? ?}
?? ?else {
?? ??? ?printf("监听网络端口成功...\n");
?? ?}
?? ?//-------IOCP Begin-------//

?? ?//4 创建完成端口 第三个参数:完成键(自定义数据结构)最后一个参数0 默认允许并发线程数量
?? ?HANDLE _completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
?? ?if (NULL == _completionPort)
?? ?{
?? ??? ?printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
?? ??? ?return -1;
?? ?}
?? ?//5 关联iocp和serversocket 第三个参数是完成键用于在关联套接字的时候传递参数

?? ?auto ret = CreateIoCompletionPort((HANDLE)sockServer, _completionPort, (ULONG_PTR)sockServer, 0);
?? ?if (!ret)
?? ?{
?? ??? ?printf("关联IOCP与ServerSocket失败,错误码=%d\n", GetLastError());
?? ??? ?return -1;
?? ?}

?? ?//6 向IOCP投递接受连接的任务
?? ?//_In_ SOCKET sListenSocket,?? ??? ?监听socket
?? ?//_In_ SOCKET sAcceptSocket,?? ??? ?主动为客户端创建socket(需要自己提前创建好)
?? ?//_Out_writes_bytes_(dwReceiveDataLength+dwLocalAddressLength+dwRemoteAddressLength) PVOID lpOutputBuffer,?? ??? ?
?? ?//_In_ DWORD dwReceiveDataLength,?? ?接收数据的长度(这个地方只要传入一个非零值,我们服务端都要求客户端必须发送一条数据上来才能完成连接,所以这里传0)
?? ?//_In_ DWORD dwLocalAddressLength,?? ?本地网络地址的长度(规定该值必须至少比正在使用的传输协议的最大地址长度多16字节)
?? ?//_In_ DWORD dwRemoteAddressLength,?? ?远端网络地址的长度 (规定该值必须至少比正在使用的传输协议的最大地址长度多16字节)
?? ?//_Out_ LPDWORD lpdwBytesReceived,?? ?实际接收到的数据长度--iocp这里没有意义不用设置该值 这个函数直接返回的?
?? ?//_Inout_ LPOVERLAPPED lpOverlapped 重叠体IOCP需要使用
?? ?IO_DATA_BASE ioData[nClient] = {};
?? ?for (int n = 0; n < nClient; n++)
?? ?{
?? ??? ?postAccept(sockServer, &ioData[n]);
?? ?}

?? ?int msgCount = 0;
?? ?while (true)
?? ?{

?? ??? ?//_In_ HANDLE CompletionPort,?? ??? ??? ??? ?创建的完成端口
?? ??? ?//_Out_ LPDWORD lpNumberOfBytesTransferred, 接收到或者发送的数据字节数
?? ??? ?//_Out_ PULONG_PTR lpCompletionKey,?? ??? ??? ?完成键 参数回传关联iocp和sockserver时候传入的
?? ??? ?//_Out_ LPOVERLAPPED* lpOverlapped,?? ??? ??? ?在向iocp投递任务传递 在 GetQueuedCompletionStatus返回
?? ??? ?//_In_ DWORD dwMilliseconds?? ??? ??? ??? ??? ?超时时间ms单位
?? ??? ?DWORD bytesTrans = 0;?? ??? ??? ??? ??? ??? ?//实际接收和发送的数据
?? ??? ?SOCKET sock = INVALID_SOCKET;
?? ??? ?IO_DATA_BASE* pIOData;

?? ??? ?if (FALSE == GetQueuedCompletionStatus(_completionPort, &bytesTrans, (PULONG_PTR)&sock, (LPOVERLAPPED*)&pIOData, 1))
?? ??? ?{
?? ??? ??? ?int err = GetLastError();
?? ??? ??? ?if (WAIT_TIMEOUT == err)
?? ??? ??? ?{ ?
?? ??? ??? ??? ?continue;
?? ??? ??? ?}
?? ??? ??? ?if (ERROR_NETNAME_DELETED == err)
?? ??? ??? ?{
?? ??? ??? ??? ?printf("关闭 sockfd=%d\n", pIOData->sockfd);
?? ??? ??? ??? ?closesocket(pIOData->sockfd);
?? ??? ??? ??? ?continue;
?? ??? ??? ?}
?? ??? ??? ?printf("GetQueuedCompletionStatus failed with error %d\n", err);
?? ??? ??? ?break;
?? ??? ?}
?? ??? ?//7.1 接受链接 完成
?? ??? ?if (IO_TYPE::ACCEPT == pIOData->iotype)
?? ??? ?{
?? ??? ??? ?printf("新客户端加入 sockfd=%d\n", pIOData->sockfd);
?? ??? ??? ?//7.2 关联IOCP与ClientSocket
?? ??? ??? ?auto ret = CreateIoCompletionPort((HANDLE)pIOData->sockfd, _completionPort, (ULONG_PTR)pIOData->sockfd, 0);
?? ??? ??? ?if (!ret)
?? ??? ??? ?{
?? ??? ??? ??? ?printf("关联IOCP与ClientSocket=%d失败\n", pIOData->sockfd);
?? ??? ??? ??? ?closesocket(pIOData->sockfd);
?? ??? ??? ??? ?continue;
?? ??? ??? ?}
?? ??? ??? ?//7.3 向IOCP投递接收数据任务
?? ??? ??? ?postRecv(pIOData);
?? ??? ?}
?? ??? ?//8.1 接收数据 完成 Completion
?? ??? ?else if (IO_TYPE::RECV == pIOData->iotype)
?? ??? ?{
?? ??? ??? ?if (bytesTrans <= 0)
?? ??? ??? ?{//客户端断开处理
?? ??? ??? ??? ?printf("关闭 sockfd=%d, RECV bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
?? ??? ??? ??? ?closesocket(pIOData->sockfd);
?? ??? ??? ??? ?continue;
?? ??? ??? ?}
?? ??? ??? ?printf("收到数据: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, ++msgCount);
?? ??? ??? ?pIOData->length = bytesTrans;
?? ??? ??? ?//8.2 向IOCP投递发送数据任务
?? ??? ??? ?postSend(pIOData);
?? ??? ?}
?? ??? ?//9.1 发送数据 完成 Completion
?? ??? ?else if (IO_TYPE::SEND == pIOData->iotype)
?? ??? ?{
?? ??? ??? ?if (bytesTrans <= 0)
?? ??? ??? ?{//客户端断开处理
?? ??? ??? ??? ?printf("关闭 sockfd=%d, SEND bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
?? ??? ??? ??? ?closesocket(pIOData->sockfd);
?? ??? ??? ??? ?continue;
?? ??? ??? ?}
?? ??? ??? ?printf("发送数据: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, msgCount);
?? ??? ??? ?//9.2 向IOCP投递接收数据任务
?? ??? ??? ?postRecv(pIOData);
?? ??? ?}
?? ??? ?else {
?? ??? ??? ?printf("未定义行为 sockfd=%d", sock);
?? ??? ?}
?? ?}

?? ?//------------//
?? ?//10.1 关闭ClientSocket
?? ?for (int n = 0; n < nClient; n++)
?? ?{
?? ??? ?closesocket(ioData[n].sockfd);
?? ?}
?? ?//10.2 关闭ServerSocket
?? ?closesocket(sockServer);
?? ?//10.3 关闭完成端口
?? ?CloseHandle(_completionPort);
?? ?//清除Windows socket环境
?? ?WSACleanup();
?? ?return 0;
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-16 22:58:21  更:2022-03-16 23:00:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:38:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码