网络编程 完成端口模型

news2024/9/25 23:11:30

1.概念以及重叠IO存在的问题

2.完成端口代码详解

整体流程

 

        使用到的新函数

        1.CreateIoCompletionPort函数

该函数创建输入/输出 (I/O) 完成端口并将其与指定的文件句柄相关联,或创建尚未与文件句柄关联的 I/O 完成端口,以便稍后关联,即创建一个新的完成端口。将打开的文件句柄的实例与 I/O 完成端口相关联,使进程能够接收涉及该文件句柄的异步 I/O 操作完成通知,即把端口和socket绑定,函数原型

HANDLE WINAPI CreateIoCompletionPort(
  _In_     HANDLE    FileHandle,
  _In_opt_ HANDLE    ExistingCompletionPort,
  _In_     ULONG_PTR CompletionKey,
  _In_     DWORD     NumberOfConcurrentThreads
);

在创建一个新的完成端口时:

参数1:填 INVALID_HANDLE_VALUE

参数2:填NULL

参数3:填0,

参数4:填0

        返回值:成功返回一个新的完成端口句柄,失败返回0

在把完成端口绑定socket时

参数1:填服务器socket

参数2:填完成端口

参数3:填绑定的socket或者对应的下标

参数4:填0

        返回值:成功返回一个与参数2一样的端口句柄,失败会返回错误码

##代码样例

	//创建完成端口
	HANDLE hPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (hPort == 0)
	{
		int a = GetLastError();
		printf("创建出错:%d\n", a);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//绑定
	HANDLE hPort1 = CreateIoCompletionPort(socketServer, hPort, 0, 0);//绑定成功 hPort1和hPort是一样的
	if (hPort1 != hPort)
	{
		int a = GetLastError();
		printf("绑定出错:%d\n", a);
		//关闭端口
		CloseHandle(hPort);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

         2.CreateThread函数

该函数创建在调用进程的虚拟地址空间内执行的线程,即创建一根线程,函数原型

HANDLE CreateThread(
  [in, optional]  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  [in]            SIZE_T                  dwStackSize,
  [in]            LPTHREAD_START_ROUTINE  lpStartAddress,
  [in, optional]  __drv_aliasesMem LPVOID lpParameter,
  [in]            DWORD                   dwCreationFlags,
  [out, optional] LPDWORD                 lpThreadId
);

参数1:控制线程句柄能否被继承,NULL表示不继承

参数2:线程栈大小,以字节为单位

参数3:线程函数地址,DWORD WINAPI ThreadProc(LPVOID lpParameter)

参数4:外部给线程传递数据

参数5:0表示线程立即执行,CREATE_SUPENDED表示线程创建完挂起状态,调用ResumeThread启动函数

参数6:线程ID,可以填NULL

        返回值:成功返回线程句柄,失败返回NULL

        3.GetQueuedCompletionStatue函数

该函数尝试从指定的 I/O 完成端口取消 I/O 完成数据包的排队。 如果没有排队的完成数据包,该函数将等待与完成端口关联的挂起 I/O 操作完成,函数原型

BOOL GetQueuedCompletionStatus(
  [in]  HANDLE       CompletionPort,
        LPDWORD      lpNumberOfBytesTransferred,
  [out] PULONG_PTR   lpCompletionKey,
  [out] LPOVERLAPPED *lpOverlapped,
  [in]  DWORD        dwMilliseconds
);

参数1:完成端口,即使用CreateIoCompletionPort函数创建的一个变量,需要从主函数传入

参数2:接收或发送的字节数, 0表示客户端下线

参数3:完成端口函数的参数3传入

参数4:接收到的重叠结构

参数5:等待时间,INFINITE一直等

        返回值:成功返回TRUE,失败返回FALSE

##完成端口整体代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include<stdio.h>
#include<Winsock2.h>
#include<mswsock.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mswsock.lib")

int PostAccept();
int PostRecv(int index);
int PostSend(int index);
DWORD WINAPI ThreadProc(LPVOID lpParameter);

#define MAX_COUNT 1024
#define MAX_RECV_COUNT 1024

SOCKET g_allsock[MAX_COUNT];
OVERLAPPED g_allIOp[MAX_COUNT];
int g_count;
char g_recvbuf[MAX_RECV_COUNT];
HANDLE hPort;//定义句柄
HANDLE* hThread;
int nProcessorsCount;

void Clear()
{
	for (int i = 0; i < g_count; i++)
	{
		if (g_allsock[i] == 0)
		{
			continue;
		}
		closesocket(g_allsock[i]);
		WSACloseEvent(g_allIOp[i].hEvent);
	}
}

BOOL WINAPI fun(DWORD dwCtrlType)
{
	switch (dwCtrlType)
	{
	case CTRL_CLOSE_EVENT:
		//释放所有soket和事件
		for (int i = 0; i < nProcessorsCount; i++)
		{
			CloseHandle(hThread[i]);
		}
		free(hThread);
		Clear();
		break;
	}
	return TRUE;
}

int main()
{
	SetConsoleCtrlHandler(fun, TRUE);
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSockMsg;
	if (0 != WSAStartup(wdVersion, &wdSockMsg))
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))
	{
		printf("版本不对\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (socketServer == INVALID_SOCKET)
	{
		printf("创建socket失败\n");
		WSACleanup();
		return 0;
	}
	//第三步 绑定IP地址和端口号
	struct sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = htons(12332);
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si)))
	{
		printf("绑定失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	g_allsock[g_count] = socketServer;
	g_allIOp[g_count].hEvent = WSACreateEvent();
	g_count++;

	//创建完成端口
	hPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (hPort == 0)
	{
		int a = GetLastError();
		printf("创建出错:%d\n", a);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//绑定
	HANDLE hPort1 = CreateIoCompletionPort((HANDLE)socketServer, hPort, 0, 0);//绑定成功 hPort1和hPort是一样的
	if (hPort1 != hPort)
	{
		int a = GetLastError();
		printf("绑定出错:%d\n", a);
		//关闭端口
		CloseHandle(hPort);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOCK_STREAM))
	{
		printf("监听失败\n");
		CloseHandle(hPort);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	if (PostAccept() != 0)
	{
		Clear();
		//清理网络库
		WSACleanup();
		return 0;
	}

	//创建线程
	SYSTEM_INFO systemProcessorsCount;//该结构体对象存放系统信息
	GetSystemInfo(&systemProcessorsCount);//该函数可以获取到系统的信息
	nProcessorsCount = systemProcessorsCount.dwNumberOfProcessors;//获取到系统的核数
	hThread = (HANDLE*)malloc(sizeof(HANDLE) * nProcessorsCount);
	if (hThread == NULL)
	{
		return 0;
	}
	for (int i = 0; i < nProcessorsCount; i++)
	{
		hThread[i] = CreateThread(NULL, 0, ThreadProc, hPort, 0, NULL);
		if (hThread[i] == NULL)
		{
			int a = GetLastError();
			printf("绑定出错:%d\n", a);
			//关闭端口
			CloseHandle(hPort);
			closesocket(socketServer);
			WSACleanup();
			return 0;
		}
	}
	//阻塞主线程
	while (1)
	{
		Sleep(1000);
	}

	//释放线程句柄
	for (int i = 0; i < nProcessorsCount; i++)
	{
		CloseHandle(hThread[i]);
	}
	free(hThread);

	CloseHandle(hPort);
	closesocket(socketServer);
	WSACleanup();

	return 0;
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	HANDLE port = (HANDLE)lpParameter;//参数1
	DWORD NumberBytes;//参数2 字节数
	ULONG_PTR index;//参数3 下标
	LPOVERLAPPED Overlapped;//参数4 重叠结构
	while (1)
	{
		BOOL bFlag = GetQueuedCompletionStatus(port, &NumberBytes, &index, &Overlapped, INFINITE);
		if (bFlag == FALSE)
		{
			int a = GetLastError();//获取错误码
			if (a == 64)
			{
				printf("force close\n");
			}
			printf("%d\n", a);
			continue;
		}
		//处理accept
		if (0 == index)
		{
			//绑定到完成端口
			HANDLE hPort1 = CreateIoCompletionPort((HANDLE)g_allsock[g_count], hPort, g_count, 0);
			if (hPort1 != hPort)
			{
				int a = GetLastError();
				printf("%d\n", a);
				closesocket(g_allsock[g_count]);
				continue;
			}
			printf("accept succee\n");
			//新客户端投递recv
			PostRecv(g_count);
			g_count++;
			PostAccept();
		}
		else
		{
			if (0 == NumberBytes)//字节数为0
			{
				//客户端下线
				//关闭
				printf("close\n");
				closesocket(g_allsock[index]);
				WSACloseEvent(g_allIOp[index].hEvent);
				g_allsock[index] = 0;
				g_allIOp[index].hEvent = NULL;
			}
			else
			{
				if (0 != g_recvbuf[0])//数组中第一个元素不为0,表示接收到消息
				{
					//recv
					printf("%s\n", g_recvbuf);
					memset(g_recvbuf, 0, MAX_RECV_COUNT);
					PostRecv((int)index);	
				}
				else
				{
					//send
					printf("send succee\n");
				}
			}
		}
	}
	return 0;
}

int PostAccept()
{
	g_allsock[g_count] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	g_allIOp[g_count].hEvent = WSACreateEvent();
	char str[1024] = { 0 };
	DWORD dwRecvcount;

	BOOL aRes = AcceptEx(g_allsock[0], g_allsock[g_count], str, 0, sizeof(struct sockaddr) + 16,
		sizeof(struct sockaddr) + 16, &dwRecvcount, &g_allIOp[0]);
	int a = WSAGetLastError();
	if (ERROR_IO_PENDING != a)
	{
		//函数出错
		return 1;
	}
	return 0;
}

int PostRecv(int index)
{
	WSABUF wsabuf;
	wsabuf.buf = g_recvbuf;
	wsabuf.len = MAX_RECV_COUNT;
	DWORD dwRecvCount;
	DWORD dwFlag = 0;

	int wRes = WSARecv(g_allsock[index], &wsabuf, 1, &dwRecvCount, &dwFlag, &g_allIOp[index], NULL);
	int a = WSAGetLastError();
	if (a != ERROR_IO_PENDING)
	{
		//延迟处理
		//函数执行出错
		printf("recv出错\n");
		return 1;
	}
	return 0;
}

int PostSend(int index)
{
	WSABUF wsabuf;//参数2
	wsabuf.buf = "你好";
	wsabuf.len = MAX_RECV_COUNT;

	DWORD dwSendCount;//参数4
	DWORD dwFlag = 0;//参数5
	int wRes = WSASend(g_allsock[index], &wsabuf, 1, &dwSendCount, dwFlag, &g_allIOp[index], NULL);
	int a = WSAGetLastError();
	if (a != ERROR_IO_PENDING)
	{
		//延迟处理
		//函数执行出错
		printf("send出错\n");
		return 1;
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129870.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

金融业务的数据存储选型

为什么用关系型数据库&#xff1f;最常见的理由是别人在用&#xff0c;所以我也得用&#xff0c;但是这个并不是理由&#xff0c;而是借口。 1 数据分类 选择数据存储类型前&#xff0c;先分析数据特点&#xff0c;才能针对性选择存储方案。 通常按数据与数据之间关系的复杂…

SSM2---spring

Spring spring环境搭建 创建一个空白模块&#xff0c;目录结构如下 在pom.xml文件中引入相关依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/…

基于SSM(Spring+SpringMVC+Mybatis)实现的个人博客系统,含数据库文件及详细说明

关于项目 该博客是基于SSM实现的个人博客系统&#xff0c;适合初学SSM和个人博客制作的同学学习。 最新版本支持用户注册&#xff0c;包含用户和管理员两个角色 。 博主已写了一篇该项目的毕业论文和录制了2个小时的代码讲解可以供大家学习&#xff0c;需要的可以联系博主&…

RFID在模块管理中的应用

应用背景 模具是工业生产的基础工艺装备&#xff0c;被称为“工业之母”。作为国民经济的基础行业&#xff0c;模具涉及机械、汽车、轻工、电子、化工、冶金、建材等各个行业&#xff0c;应用范围十分广泛。模具资产管理采用传统的人工纸质记录的方式已经无法及时有效的进行&am…

还在用 XShell - 试试 IntelliJ IDEA 的 SSH

SSH 是很多人用得不多&#xff0c;但是又不得不用的工具。 如果你不是搞运维&#xff0c;没有必要搞个 CRT&#xff0c;XShell 也够用了&#xff0c;但是这 2 个都是收费软件&#xff0c;同时还不太便宜。 试试 IDEA 的 SSH 其实 IntelliJ IDEA 已经提供了 SSH 的功能。 如…

053-线程的状态改变及线程同步详细介绍

【上一讲】051-java线程的2种实现方法详解_CSDN专家-赖老师(软件之家)的博客-CSDN博客 线程可以处于以下四个状态之一1.新建(new):线程对象已经建立,但还没有启动,所以他不能运行。2.就绪(runnable):这种状态下,只要调度程序把时间片分配给线程就可以执行,也就是说…

10分钟带你了解什么是ArrayBuffer?

前言 很多时候我们前端开发是用不到 ArrayBuffer 的&#xff0c;但是用不到 ArrayBuffer 不代表我们不需要了解这个东西。本文就围绕 ArrayBuffer 来讲一下相关知识&#xff0c;大概需要10分钟左右就可以读完本文。 什么是ArrayBuffer&#xff1f; 描述 ArrayBuffer 对象用…

Paramiko库讲解

目录 基本概念 Paramiko组件架构 Key handing类 Transport类 SFTPClient类 SSHClient类—主要使用的类 Python编写完整例子 基本概念 Paramiko是Python实现SSHv2协议的模块&#xff0c;支持口令认证和公钥认证两种方式 通过Paramiko可以实现通过Python进行安全的远程命…

Html5网页播放器的同层播放功能

Html5网页播放器的同层播放功能&#xff1a; 在Android手机上使用H5播放视频时&#xff0c;大多数的国内浏览器厂商都会在视频播放时劫持<video>标签&#xff0c;使用浏览器自带的播放器播放视频&#xff0c;而且播放器会处于最高层级&#xff0c;视频上面无法显示其它h…

数影周报:字节跳动员工违规获取TikTok用户数据,阿里组织调整

本周看点&#xff1a;字节跳动员工违规获取TikTok用户数据&#xff1b;钉钉宣布用户数破6 亿&#xff1b;阿里组织调整&#xff1b;星尘数据完成 5000 万元 A 轮融资...... 数据安全那些事 字节跳动员工违规获取TikTok用户数据 字节跳动旗下热门应用TikTok日前曝出严重风波。字…

郭德纲落选,冯巩、赵炎上榜,国家非物质文化遗产传承人评选落幕

根据国家广电局26日消息&#xff0c;经过激烈的竞争&#xff0c;国家非物质文化遗产传承人评选工作&#xff0c;已经顺利落下帷幕。 在此次评选活动当中&#xff0c;评委会一致审议通过&#xff0c;著名相声演员冯巩和赵炎&#xff0c;被评为了非物质文化遗产传承人。而呼声很高…

Linux | 进程理解 | 进程的终止,等待与替换 | 环境变量的介绍与使用

文章目录进程终止进程终止的方法操作系统是怎么终止进程的&#xff1f;进程等待为何需要等待进程&#xff1f;怎么等待一个进程&#xff1f;非阻塞式等待进程替换什么是进程替换&#xff1f;为什么要进程替换&#xff1f;怎样替换一个进程&#xff1f;exec系列函数环境变量用命…

企业微信开发(一)常见问题收集及解决方案

持续收集企业微信开发中遇到疑难杂症&#xff0c;并给出相应的解决方案 一、好友上限&#xff08;84061&#xff09; 背景&#xff1a;达到添加好友数上限的员工&#xff0c;新增自动通过的好友&#xff0c;无法拉取到客户信息。 根因&#xff1a;企业微信业务限制 员工添加的…

启辰全面转型新能源,能否创造风日产第二增长曲线?

随着新能源汽车市占率的不断增长&#xff0c;传统汽车大厂加大了在新能源汽车领域的布局。这其中决心最大的当属东风日产&#xff0c;第二品牌——“启辰”全面转型新能源&#xff0c;告别纯然有车开发。 12月30日&#xff0c;以“新科技 新生活”为主题的第二十届广州车展盛大…

Linux系统定时信号SIGALRM的触发与alarm函数的使用

1、 定时信号SIGALRM的用途 在编程的过程中&#xff0c;很多时候我们需要为程序设置一个闹钟&#xff0c;然后到了闹钟设定的时刻然后再去采取相关的操作。比如进行socket编程时&#xff0c;如果客户端长时间没有与服务器进行交互&#xff0c;需要服务器在一定时间之后主动关闭…

Linux | 权限管理

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a;《CGod的个人主页》\color{Darkorange}{《CGod的个人主页》}《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a;《编程成神技术交流社区》\color{Darkorange}{《编程成神技术交流社区》…

Vue(二)

1. 模板语法 1.1 实现效果 1.2 模板的理解 不加v-bind就相当于直接把双引号里的东西当成字符串执行&#xff0c;加了bind双引号里的东西就当成js表达式执行 v-bind:可以简写成&#xff1a; 起始标签和结束标签中间夹着的就是标签体 <!DOCTYPE html> <html><hea…

【数据结构】插入排序、希尔排序、冒泡排序、选择排序

文章目录 一、直接插入排序 思想 程序代码 时间复杂度 二、希尔排序 思想 程序代码 时间复杂度 三、冒泡排序 思想 程序代码 时间复杂度 四、选择排序 思想 程序代码 时间复杂度 一、直接插入排序 思想 直接插入排序有些类似于我们玩扑克牌时的整理牌序动作&a…

目标检测中常见的神经网络组成层------Pytorch

物体检测中常见的神经网络组成层 文章目录物体检测中常见的神经网络组成层卷积层激活函数层池化层Dropout层全连接层常见的物体检测算法常用卷积层、池化层、全连接层、激活函数层、Dropout层。 卷积层 CNN–各层的介绍_Miracle Fan的博客-CSDN博客_cnn各层介绍 在pytorch中…

Python3入门基础(08)一个函数

Python3 函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。 函数能提高应用的模块性&#xff0c;和代码的重复利用率。你已经知道Python提供了许多内建函数&#xff0c;比如print()。但你也可以自己创建函数&#xff0…