见到的一篇IOCP流程 自己用demo实现了一下, 简单照抄,改动了一点点

摘要:
(sockaddr*)&GlobalAlloc(GPTR;CreateIoCompletionPort((HANDLE)pPerHandle->DWORDdwFlags=0;dwFlags;(在线程函数while循环中)pPerIO->printf(pPerIO->buf.buf=pPerIO-˃nOperationType=OP_READ;
要分析的实例分为两个线程:


分别是主线程(MAIN),还有一个是创建的线程(ServerThread)


1.主函数完成初始化工作:
  1.1: (主线程)HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);    创建完成端口对象
  1.2: (主线程)::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);  创建线程用于接收等
  1.3:(主线程)调用socket(),SOCKADDR_IN,bind(),listen(),初始化套接字
  1.4: (线程函数)HANDLE hCompletion = (HANDLE)lpParam;   通过线程参数得到完成端口对象
  1.5: (线程函数) while(TRUE)     定义循环  循环等待套接字上发生事件
  1.6:(线程函数) ::GetQueuedCompletionStatus() 在关联到此完成端口的所有套节字上等待I/O完成
  1.7: (主线程) SOCKADDR_IN saRemote; SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen); 为新连接建立结构并等待连接请求


2.有连接发生:
  2.1: (主线程while循环中) PPER_HANDLE_DATA pPerHandle = (PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
                           创建PPER_HANDLE_DATA结构 
  2.2: (主线程while循环中) ::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
    完成 完成端口 与 套接字关联
  2.3: (主线程while循环中)    给pPerIO结构   添加类型
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType = OP_READ;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
  2.4: (主线程while循环中) ::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);     发送异步接收请求
  2.5: (主线程while循环中)  回到WHILE循环accept()函数处  等待新连接
  2.6: (线程函数while循环中)GetQueuedCompletionStatus() 此时I/O完成 开始处理消息
  2.7: (线程函数while循环中)pPerIO->nOperationType  通过类型判断  消息类型
  2.7:(线程函数while循环中)打印接收的消息  并在此投递  一个完成端口
pPerIO->buf[dwTrans] = '\0';
printf(pPerIO -> buf);

WSABUF buf;
buf.buf = pPerIO->buf ;
buf.len = BUFFER_SIZE;
pPerIO->nOperationType = OP_READ;
DWORD nFlags = 0;
printf("12\n");
::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);
  2.8:(线程函数while循环中)  由于又有端口I/O完成 ::GetQueuedCompletionStatus()函数  继续判断并处理
  2.9:(线程函数while循环中)  由于没有数据 GetQueuedCompletionStatus()返回值为错误  调用一下函数关闭
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);

  2.10:(线程函数while循环中)  在此回到while循环 调用::GetQueuedCompletionStatus()函数  继续等待


// iocpTP.cpp : Defines the entry point for the console application.
//


#include "stdafx.h"


#include <stdio.h>
#include <windows.h>


// 初始化Winsock库
#include <winsock2.h>
#pragma comment(lib,"WS2_32.lib")// 






#define BUFFER_SIZE 1024


typedef struct _PER_HANDLE_DATA// per-handle数据
{
SOCKET s; // 对应的套节字句柄
sockaddr_in addr;// 客户方地址
} PER_HANDLE_DATA, *PPER_HANDLE_DATA;




typedef struct _PER_IO_DATA// per-I/O数据
{
OVERLAPPED ol;// 重叠结构
char buf[BUFFER_SIZE];// 数据缓冲区
int nOperationType;// 操作类型
#define OP_READ   1
#define OP_WRITE  2
#define OP_ACCEPT 3
} PER_IO_DATA, *PPER_IO_DATA;




DWORD WINAPI ServerThread(LPVOID lpParam)
{
// 得到完成端口对象句柄
HANDLE hCompletion = (HANDLE)lpParam;
printf("ServerThread Processor\n");
DWORD dwTrans;
PPER_HANDLE_DATA pPerHandle;
PPER_IO_DATA pPerIO;
while(TRUE)
{
// 在关联到此完成端口的所有套节字上等待I/O完成
printf("wait GetQueuedCompletionStatus  Done \n");
BOOL bOK = ::GetQueuedCompletionStatus(hCompletion, 
&dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);
if(!bOK) // 在此套节字上有错误发生
{
printf("9\n");
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}

if(dwTrans == 0 &&// 套节字被对方关闭
(pPerIO->nOperationType == OP_READ || pPerIO->nOperationType == OP_WRITE))

{
printf("10\n");
::closesocket(pPerHandle->s);
::GlobalFree(pPerHandle);
::GlobalFree(pPerIO);
continue;
}
printf("11\n");
switch(pPerIO->nOperationType)// 通过per-I/O数据中的nOperationType域查看什么I/O请求完成了
{
case OP_READ: // 完成一个接收请求
{
pPerIO->buf[dwTrans] = '\0';
printf(pPerIO -> buf);

// 继续投递接收I/O请求
WSABUF buf;
buf.buf = pPerIO->buf ;
buf.len = BUFFER_SIZE;
pPerIO->nOperationType = OP_READ;


DWORD nFlags = 0;
printf("12\n");
::WSARecv(pPerHandle->s, &buf, 1, &dwTrans, &nFlags, &pPerIO->ol, NULL);

}
break;
case OP_WRITE: // 本例中没有投递这些类型的I/O请求
{
WSABUF buf;
buf.buf = pPerIO->buf ;
buf.len = BUFFER_SIZE;
pPerIO->nOperationType = OP_READ;
DWORD nFlags = 0;
::WSASend(pPerHandle->s, &buf, 1, &dwTrans, nFlags, &pPerIO->ol, NULL);
}
break;
case OP_ACCEPT:
{
}
break;
}
}
return 0;
}




void main()
{
// 初始化WS2_32.dll
BYTE minorVer = 0x02;
BYTE majorVer = 0x02;
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
}
int nPort = 5000;
// 创建完成端口对象,创建工作线程处理完成端口对象中事件
printf("Init NetWork\n");
HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
printf("CreateIoCompletionPort Done\n");
::CreateThread(NULL, 0, ServerThread, (LPVOID)hCompletion, 0, 0);
printf("Create Server Thread \n");
// 创建监听套节字,绑定到本地地址,开始监听
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(nPort);
si.sin_addr.S_un.S_addr = INADDR_ANY;
::bind(sListen, (sockaddr*)&si, sizeof(si));
::listen(sListen, 5);


printf("Start Listen \n");
// 循环处理到来的连接
while(TRUE)
{
// 等待接受未决的连接请求
printf("Wait for Connect...\n");
SOCKADDR_IN saRemote;
int nRemoteLen = sizeof(saRemote);
SOCKET sNew = ::accept(sListen, (sockaddr*)&saRemote, &nRemoteLen);


// 接受到新连接之后,为它创建一个per-handle数据,并将它们关联到完成端口对象。
PPER_HANDLE_DATA pPerHandle = 
(PPER_HANDLE_DATA)::GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
pPerHandle->s = sNew;
memcpy(&pPerHandle->addr, &saRemote, nRemoteLen);
::CreateIoCompletionPort((HANDLE)pPerHandle->s, hCompletion, (DWORD)pPerHandle, 0);
printf("Accept connect , assigned a CreateIoCompletionPort \n");
// 投递一个接收请求
PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
pPerIO->nOperationType = OP_READ;
WSABUF buf;
buf.buf = pPerIO->buf;
buf.len = BUFFER_SIZE;
DWORD dwRecv;
DWORD dwFlags = 0;
printf(" Mail a request!!! \n");
::WSARecv(pPerHandle->s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);
}


::WSACleanup();
}


测试程序:

//////////////////////////////////////////////////////////
// TCPClient.cppÎļþ
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")


int main()
{
BYTE minorVer = 0x02;
BYTE majorVer = 0x02;
//WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}


// ´´½¨Ì×½Ú×Ö
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf(" Failed socket() \n");
return 0;
}

// Ò²¿ÉÒÔÔÚÕâÀïµ÷ÓÃbindº¯Êý°ó¶¨Ò»¸ö±¾µØµØÖ·
// ·ñÔòϵͳ½«»á×Ô¶¯°²ÅÅ

// ÌîдԶ³ÌµØÖ·ÐÅÏ¢
sockaddr_in servAddr; 
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(5000);
// ×¢Ò⣬ÕâÀïÒªÌîд·þÎñÆ÷³ÌÐò£¨TCPServer³ÌÐò£©ËùÔÚ»úÆ÷µÄIPµØÖ·
// Èç¹ûÄãµÄ¼ÆËã»úûÓÐÁªÍø£¬Ö±½ÓʹÓÃ127.0.0.1¼´¿É
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf(" Failed connect() \n");
return 0;
}

// ·¢ËÍÊý¾Ý
send(s,"dddd",sizeof("dddd"),0);
// ½ÓÊÕÊý¾Ý
char buff[256];
int nRecv = ::recv(s, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf(" ½ÓÊÕµ½Êý¾Ý£º%s", buff);
}

// ¹Ø±ÕÌ×½Ú×Ö
::closesocket(s);


::WSACleanup();
return 0;
}

免责声明:文章转载自《见到的一篇IOCP流程 自己用demo实现了一下, 简单照抄,改动了一点点》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PHP如何打造一个高可用高性能的网站呢?Postman高级应用——串行传参和动态传参详解(转发)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ISCSI共享存储

ISCSI网络磁盘    默认端口:3260 服务端: 一. 二.安装软件:targetcli 用命令targetcli进行配置------------------------进入iscsi磁盘配置模式   1.建立backstore后端存储     >>>  backstores/block create  name=nsd  dev=...

IP address could not be resolved: Name or service not known

[root@test ~]# /usr/local/mysql/bin/mysqld2018-08-05T07:00:33.647509Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the...

单进程单线程的Redis如何能够高并发

1、基本原理 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗) (1)为什么不采用多进程或多线程处理? 多线程处理可能涉及到锁 多线程处理会涉及到线程切换而消耗CPU (2)单线程处理的缺点? 无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来完善 2、Redis不存在线程安全问题? Redis采...

结构体数组(C++)

1.定义结构体数组 和定义结构体变量类似,定义结构体数组时只需声明其为数组即可。如: struct Student{ int num; char name[20]; char sex[5]; int age; float score; char addr[30]; }; Stude...

微信 ios端config配置失败 android端正常

<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> 如果你页面启用了https,务必引入 https://res.wx.qq.com/open/js/jweixin-1.0.0.js ,否则将无法在iOS9.0以上系统中成功使用JSSDK...

解决关闭窗口,C#报错"在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke"

情况:在C#开发的过程中多线程委托是经常用的,今天在测试以前写的软件的时候发现有个问题,报 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。 这样的错误。 解决方法:加上 if (this.IsHandleCreated) 1、首先分析问题,句柄:是对象的引用名,存于栈区(可以理解为对象的指针),对象是存于堆区,通过操控栈区...