网络编程 事件选择模型

news2025/1/11 21:46:19

目录

1.概念分析

2.事件选择代码逻辑

 1.WSACreateEvent函数

2.WSACloseEvent函数

3.WSAEventSelect函数

4.WSAWaitForMultipleEvents()函数

5.WSAEnumNetworkEvents函数

        事件分类

3.##模型代码样例


1.概念分析

        本质上是操作系统处理用户行为,详细如下

事件选择模型是select模型的升级版,区别select模型的代码执行时,会在socket数组中阻塞其它的操作,而事件选择模型不会出现这样的情况,在系统处理消息队列时,用户是可以执行其它操作的,这里就是异步。事件选择模型中的事件在事件数组中是无序的。

2.事件选择代码逻辑

基本流程

 

        事件选择模型步骤前面的代码与select模型代码是一致的

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

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

int main()
{
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocketMsg;
	int nRes = WSAStartup(wdVersion, &wdSocketMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (HIBYTE(wdSocketMsg.wVersion) != 2 || LOBYTE(wdSocketMsg.wVersion) != 2)
	{
		printf("网络库版本出错\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	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;
	}
	//第五步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		printf("监听失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	
}

核心代码步骤有4步:1.创建事件对象,使用函数 WSACreateEvent

                                   2.为每个事件对象绑定socket

                                   3.查看事件是否有信号,使用函数 WSAWaitForMultipleEvent

                                   4.有信号时对信号进行分类,使用函数 WSAEnumNetworkEvents

 1.WSACreateEvent函数

该函数创建一个事件对象,函数原型

WSAEVENT WSAAPI WSACreateEvent();

        返回值:如果创建成功会返回事件对象的句柄,如果创建失败返回 WSA_INVALID_EVENT,本质上是一个空指针

##代码样例

	//创建事件
	WSAEVENT eventServer = WSACreateEvent();//创建成会返回一个事件
	if (WSA_INVALID_EVENT == eventServer)
	{
		//出错了
		int a = WSAGetLastError();//获取错误码
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

2.WSACloseEvent函数

该函数关闭创建好的事件,即关闭打开的事件对象句柄,函数原型

BOOL WSAAPI WSACloseEvent(
  [in] WSAEVENT hEvent
);

参数 hEvent:事件对象

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

3.WSAEventSelect函数

该函数指定要与指定的FD_XXX网络事件集关联的事件对象,即绑定并投递事件。函数原型

int WSAAPI WSAEventSelect(
  [in] SOCKET   s,
  [in] WSAEVENT hEventObject,
  [in] long     lNetworkEvents
);

 参数1 s:要被绑定的socket

参数2 hEventObject:要关联的事件对象

参数3 lNetworkEvents:具体事件

        参数3具有的常用具体事件如下:

事件意义用法
FD_ACCEPT处理客户端连接问题与服务器socket绑定
FD_READ处理客户端发送的消息

与客户端socket绑定

可多个属性并列 使用 |

FD_CLOSE处理客户端下线问题

与客户端socket绑定

处理的包含强制下线和正常下线

FD_WRITE处理服务器给客户端发消息

与客户端socket绑定

会在accept后立即主动产生该信号

        返回值:成功返回0,失败返回SOCKET_ERROR

4.WSAWaitForMultipleEvents()函数

该函数返回一个或多个指定事件对象处于信号状态、超时间隔过期或 I/O 完成例程执行的时间。函数原型

DWORD WSAAPI WSAWaitForMultipleEvents(
  [in] DWORD          cEvents,
  [in] const WSAEVENT *lphEvents,
  [in] BOOL           fWaitAll,
  [in] DWORD          dwTimeout,
  [in] BOOL           fAlertable
);

参数1 cEvents:要处理事件的数量

参数2 lphEvents:保存事件的数组

参数3 fWaitAll:事件等待方式,为TRUE时,数组中所有事件产生信号时才返回,为FALSE时,任何一个事件产生信号,立即返回

参数4 dwTimeout:设定等待时间,单位是毫秒;填数字a时,等待a毫秒后,超时返回 WSA_WAIT_TIMEOUT;填0时,检查事件对象的状态并立即返回,不管有没有信号;填 WSA_INFINITE时,等待直到事件发生

参数5 fAlertable:在这个模型中填FALSE

        返回值:

如果成功返回以下值中的一个:

返回值含义
数组下标的运算值参数3为false时,返回值减去WSA_WAIT_EVENT_0==数组中事件的下标
WSA_WAIT_IO_COMPLETION参数5为true才会返回该值
WSA_WAIT_TIMEOUT超时会返回该值

如果失败返回 WSA_WAIT_FAILED,可使用 WSAGetLastError 获取错误码

##代码实例

//创建结构体对象
	struct fd_es_set esAll = { 0, {0}, {NULL} };
	
	//创建事件
	WSAEVENT eventServer = WSACreateEvent();//创建成会返回一个事件
	if (WSA_INVALID_EVENT == eventServer)
	{
		//出错了
		int a = WSAGetLastError();//获取错误码
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//把事件和socket装进结构体中
	esAll.eventAll[esAll.count] = eventServer;
	esAll.socketAll[esAll.count] = socketServer;
	esAll.count++;

	while (1)
	{
		//等待事件
		DWORD wRes = WSAWaitForMultipleEvents(esAll.count, esAll.eventAll, FALSE, WSA_INFINITE, FALSE);
		if (wRes == WSA_WAIT_FAILED)
		{
			//出错了
			int a = WSAGetLastError();//获取错误码
			printf("错误码:%d\n", a);
			break;
		}
		//超时判断,WSAWaitForMultipleEvents函数的参数4需要是具体毫秒
		if (wRes == WSA_WAIT_TIMEOUT)
		{
			continue;
		}

		DWORD nIndex = wRes - WSA_WAIT_EVENT_0;//拿到事件在数组中的下标
	}

	//绑定事件
	if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT))//成功返回0
	{
		//绑定失败
		int a = WSAGetLastError();//获取错误码
		WSACloseEvent(eventServer);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//释放事件 句柄
	WSACloseEvent(eventServer);

5.WSAEnumNetworkEvents函数

该函数可发现指示套接字的网络事件发生、清除内部网络事件记录以及重置事件对象,即获取事件类型,并将事件上的信号重置,函数原型

int WSAAPI WSAEnumNetworkEvents(
  [in]  SOCKET             s,
  [in]  WSAEVENT           hEventObject,
  [out] LPWSANETWORKEVENTS lpNetworkEvents
);

参数1 s:需要处理的对应socket

参数2 hEventObject:需要处理的对应事件

参数3 lpNetworkEvents:这是一个结构体类型,当事件处理发生错误时,在这个变量中可以找到对应socket发生错误的错误码,结构体原型如下

typedef struct _WSANETWORKEVENTS {
  long lNetworkEvents;
  int  iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;

内置对应事件的错误数组iErrorCode,

        返回值:处理成功返回0,失败返回SOCKET_ERROR

##代码实例

		DWORD nIndex = wRes - WSA_WAIT_EVENT_0;//拿到事件在数组中的下标
		//得到下标对应的具体操作
		WSANETWORKEVENTS NetWorkEvents;
		if (SOCKET_ERROR == WSAEnumNetworkEvents(esAll.socketAll[nIndex], esAll.eventAll[nIndex], &NetWorkEvents))
		{
			//出错了
			int a = WSAGetLastError();
			printf("事件操作出错,错误码:%d\n", a);
			break;
		}

        事件分类

在事件分类这一部份代码中,每一个信号都可以对应一种事件,在服务器中FD_ACCEPT信号会对应accept事件,FD_WRITE信号会对应send事件,FD_READ会对应recv事件,在这一部分代码中使用的是if语句,尽量不使用switch语句和if else语句

关键判断语句

if (NetWorkEvents.lNetworkEvents & FD_ACCEPT)//使用按位与判断是否是对应事件
		{
			if (0 == NetWorkEvents.iErrorCode[FD_ACCEPT_BIT])//判断如果错误数组中对应下标的位置为0,则表示没有错误,否则该下标位置存放对应错误码
                {

                }
        }

3.##模型代码样例

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

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

//定义事件和socket数组的结构体
struct fd_es_set
{
	unsigned short count;
	SOCKET socketAll[WSA_MAXIMUM_WAIT_EVENTS];//WSA_MAXIMUM_WAIT_EVENTS 这是一个宏  大小是 64
	WSAEVENT eventAll[WSA_MAXIMUM_WAIT_EVENTS];
};


int main()
{
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocketMsg;
	int nRes = WSAStartup(wdVersion, &wdSocketMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (HIBYTE(wdSocketMsg.wVersion) != 2 || LOBYTE(wdSocketMsg.wVersion) != 2)
	{
		printf("网络库版本出错\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	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;
	}
	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		printf("监听失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//创建结构体对象
	struct fd_es_set esAll = { 0, {0}, {NULL} };
	
	//创建事件
	WSAEVENT eventServer = WSACreateEvent();//创建成会返回一个事件
	if (WSA_INVALID_EVENT == eventServer)
	{
		//出错了
		int a = WSAGetLastError();//获取错误码
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//绑定事件
	if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT))//成功返回0
	{
		//绑定失败
		int a = WSAGetLastError();//获取错误码
		WSACloseEvent(eventServer);
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}

	//把事件和socket装进结构体中
	esAll.eventAll[esAll.count] = eventServer;
	esAll.socketAll[esAll.count] = socketServer;
	esAll.count++;

	while (1)
	{
		//等待事件
		DWORD wRes = WSAWaitForMultipleEvents(esAll.count, esAll.eventAll, FALSE, WSA_INFINITE, FALSE);
		if (wRes == WSA_WAIT_FAILED)
		{
			//出错了
			int a = WSAGetLastError();//获取错误码
			printf("等待事件,错误码:%d\n", a);
			break;
		}
		//超时判断,WSAWaitForMultipleEvents函数的参数4需要是具体毫秒
		if (wRes == WSA_WAIT_TIMEOUT)
		{
			continue;
		}

		DWORD nIndex = wRes - WSA_WAIT_EVENT_0;	//拿到事件在数组中的下标
		//得到下标对应的具体操作
		WSANETWORKEVENTS NetWorkEvents;
		//如果发生错误会把对应错误码放进NetWorkEvents结构体数组中
		if (SOCKET_ERROR == WSAEnumNetworkEvents(esAll.socketAll[nIndex], esAll.eventAll[nIndex], &NetWorkEvents))
		{
			//出错了
			int a = WSAGetLastError();
			printf("事件操作出错,错误码:%d\n", a);
			break;
		}

		//事件分类
		if (NetWorkEvents.lNetworkEvents & FD_ACCEPT)//使用按位与判断是否是对应事件
		{
			if (0 == NetWorkEvents.iErrorCode[FD_ACCEPT_BIT])//判断如果错误数组中对应下标的位置为0,则表示没有错误
			{
				//接受连接
				SOCKET socketClient = accept(socketServer, NULL, NULL);
				if (INVALID_SOCKET == socketClient)
				{
					//创建客户端socket失败
					continue;
				}
				//创建事件
				WSAEVENT wsaClientEvent = WSACreateEvent();
				if (wsaClientEvent == WSA_INVALID_EVENT)
				{
					closesocket(socketClient);
					continue;
				}
				//投放事件给系统
				if (SOCKET_ERROR == WSAEventSelect(socketClient, wsaClientEvent, FD_CLOSE | FD_READ | FD_WRITE))
				{
					closesocket(socketClient);
					WSACloseEvent(wsaClientEvent);
					continue;
				}
				//放进结构体
				esAll.eventAll[esAll.count] = wsaClientEvent;
				esAll.socketAll[esAll.count] = socketClient;
				esAll.count++;
				printf("accept succee\n");
			}
			else
			{
				//否则继续
				continue;
			}
		}
		if (NetWorkEvents.lNetworkEvents & FD_WRITE)//FD_WRITE信号对应的事件是send
		{
			if (0 == NetWorkEvents.iErrorCode[FD_WRITE_BIT])//在创建的时候会被触发一次 可以用来对数据进行初始化
			{
				//
				if (SOCKET_ERROR == send(esAll.socketAll[nIndex], "connect succee", (int)strlen("connect succee"), 0))
				{
					int a = WSAGetLastError();
					printf("发送出错,错误码:%d\n", a);
					continue;
				}
				printf("write succee\n");
			}
			else
			{
				printf("事件触发错误,错误码:%d\n", NetWorkEvents.iErrorCode[FD_WRITE_BIT]);
				continue;
			}
		}
		if (NetWorkEvents.lNetworkEvents & FD_READ)
		{
			if (0 == NetWorkEvents.iErrorCode[FD_READ_BIT])//FD_READ信号对应的事件是recv
			{
				char strRecv[1500] = { 0 };
				if (SOCKET_ERROR == recv(esAll.socketAll[nIndex], strRecv, 1499, 0))
				{
					int a = WSAGetLastError();
					printf("发送出错,错误码:%d\n", a);
					continue;
				}
				printf("read data:%s\n", strRecv);
			}
			else
			{
				continue;
			}
		}
		if (NetWorkEvents.lNetworkEvents & FD_CLOSE)
		{
			if (0 == NetWorkEvents.iErrorCode[FD_CLOSE_BIT])//处理关闭的客户端以及对应事件
			{
				printf("client close\n");
				//清理下线的客户端 套接字 事件
				//套接字
				closesocket(esAll.socketAll[nIndex]);
				esAll.socketAll[nIndex] = esAll.socketAll[esAll.count - 1];
				//事件
				WSACloseEvent(esAll.eventAll[nIndex]);
				esAll.eventAll[nIndex] = esAll.eventAll[esAll.count - 1];

				//数量减一
				esAll.count--;
			}
			else
			{
				printf("关闭错误码:%d\n", NetWorkEvents.iErrorCode[FD_CLOSE_BIT]);
				//清理下线的客户端 套接字 事件
				//套接字
				closesocket(esAll.socketAll[nIndex]);
				esAll.socketAll[nIndex] = esAll.socketAll[esAll.count - 1];
				//事件
				WSACloseEvent(esAll.eventAll[nIndex]);
				esAll.eventAll[nIndex] = esAll.eventAll[esAll.count - 1];

				//数量减一
				esAll.count--;
			}
		}

	}

	//释放事件 句柄
	WSACloseEvent(eventServer);

	closesocket(socketServer);
	WSACleanup();

	return 0;

	
}

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

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

相关文章

目标检测之YOLOv5算法分析

YOLOv5共有5个版本的网络模型及其权重文件&#xff0c;即&#xff08;n,s,m,l,x&#xff09;。 &#xff08;下图来自github上yolov5官方开源项目的性能截图&#xff09; 其中n,s,m,l,x网络模型结构如出一辙&#xff0c;差异在参数上。另外的n6,s6,m6,l6,x6模型是对于更大分辨…

人生黄金十年,你有考虑来社科院与杜兰大学金融管理硕士项目汲取能量吗

在人生长河中&#xff0c;你觉得黄金的十年是哪个阶段呢&#xff1f;在一篇文章中看到人生最宝贵的十年&#xff0c;就是30岁到40岁这十年&#xff0c;一切都未确定&#xff0c;一切都还有机会&#xff0c;这个年龄段&#xff0c;寸阴寸金&#xff0c;流金年华&#xff0c;应该…

FFmpeg学习笔记--视频传输的基本概念

目录 1--容器&#xff08;container&#xff09;和文件&#xff08;file&#xff09; 2--媒体流&#xff08;stream&#xff09; 3--数据帧&#xff08;frame&#xff09;和数据包&#xff08;packet&#xff09;&#xff1a; 4--编解码器&#xff08;Codec&#xff09; 5…

7万人随访发现,每周高强度运动15分钟,死亡风险降低24%

*仅供医学专业人士阅读参考我们已经知道&#xff0c;无论是日常规律运动&#xff0c;还是周末集中一两天运动&#xff0c;只要每周能完成至少75-150分钟的高强度有氧运动&#xff0c;都可以降低全因死亡率和特定原因死亡率[1]。每周300-600分钟中强度运动或150-300分钟高强度运…

RocketMQ原理篇

文章目录broker与NameServerMessageQueue与Topic的关系生产者、消费者写入读取 消息CommitLog生产者消费者组broker与NameServer 基于 Dledger 实现 RocketMQ 高可用自动切换 broker 会每隔 30 秒向 NameServer 发送一个的心跳 &#xff0c;NameServer 收到一个心跳 会更新对…

kafka再浅析

在日常开发中&#xff0c;经常使用kafka&#xff0c;对它是既熟悉又陌生&#xff0c;下面继续聊&#xff0c;继续总结。 1、消息中间件 分布式消息是一种通信机制&#xff0c;和RPC、HTTP不一样&#xff0c;消息中间件采用分布式中间代理的方式进行通信。采用消息中间件后&…

MarkDown语法(自用)

目录结构展示 tree -a 显示所有tree -d 只显示文档夹tree -L n 显示项目的层级&#xff0c;n表示层级数&#xff0c;比如想要显示项目三层结构&#xff0c;可以用tree -l 3&#xff1b;tree -I pattern 用于过滤不想要显示的文档或者文档夹。比如你想要过滤项目中的 node_modu…

Linux之(17)系统服务

Linux之(17)系统服务 Author&#xff1a;onceday Date&#xff1a;2022年12月24日 漫漫长路&#xff0c;有多少人对你笑过… 参考文档&#xff1a; Systemd 入门教程&#xff1a;命令篇 - 阮一峰的网络日志 (ruanyifeng.com)可能是史上最全面易懂的 Systemd 服务管理教程&a…

MergeTree原理之一级索引

一级索引 MergeTree的主键使用PRIMARY KEY定义&#xff0c;待主键定义之后&#xff0c;MergeTree会依据index_granularity间隔&#xff08;默认8192行&#xff09;&#xff0c;为数据表生成一级索引并保存至primary.idx文件内&#xff0c;索引数据按照PRIMARY KEY排序。相比使…

【PotPlayer】采集Switch图像及录制

【PotPlayer】采集Switch图像及录制下载potplayer使用方法连接设备录制视频无边框设置阳&#xff0c;休&#xff0c;懂&#xff1f;QAQ。阳之前买了个Switch&#xff0c;正好有好玩的想录下来&#xff0c;然后就…自行某宝&#xff0c;某东去买个采集卡。本文只管连软件&#x…

【运维有小邓】ADSelfService Plus身份管理

一、身份管理挑战&#xff1a; 由于企业需要越来越高的安全性&#xff0c;以保护用户帐户免遭入侵者的任何恶意攻击&#xff0c;因此身份管理正日益变得重要。在所有密码相关的身份挑战中&#xff0c;帮助台工作单量成为重中之重&#xff0c;它们在组织的年同比财务预算中不堪…

(二)ElasticSearch使用

一、ES的基本使用 1.创建索引 创建一个test索引http://localhost:9200/test 2.删除索引 http://localhost:9200/test 3.查看索引 http://localhost:9200/_all 4.向索引中新增数据 http://localhost:9200/person/_doc/ 5.搜索数据 http://localhost:9200/person/_doc/_sear…

全志Tina Linux SPINAND UBI 离线烧录 开发指南 支持百问网T113 D1-H哪吒 DongshanPI-D1s V853-Pro等开发板

1 概述 编写目的&#xff1a; 介绍Sunxi SPINand 烧写时的数据布局 2 名词解释 词义UBIunsorted block imagePEBphysical erase blockLEBlogical erase block PEB 和logical block 关系 1 PEB 1 logical block 1 logical block 2 physical blocks3 总体数据布局 ubi 方案…

必读,一文普及MES系统知识

MES系统的基本概念制造执行系统&#xff08;MES&#xff09;是一套面向制造企业的信息管理系统。MES系统可以为企业提供管理模块&#xff0c;包括制造数据管理、计划与调度管理、生产调度管理、库存管理、质量管理、生产过程控制、底层数据集成分析、上层数据集成与分解&#x…

LVGL学习笔记5 - Display, Screen和Layer

目录 1. Display 2. Screen 2.1 创建Screen 2.2 加载Screen 2.3 获取活动的Screen 2.4 实例 2.4.1 定义2个Screen全局变量 2.4.2 初始化Screen 2.4.3 循环更替 3. Layer图层 3.1 切换顺序 3.2 顶层和系统层 3.3 实例 3.3.1 创建全局变量 3.3.2 初始化 3.3.3 …

【OpenFOAM】-olaFlow-算例5- oppositeSolitariesFlume

算例路径&#xff1a; olaFlow\tutorials\oppositeSolitariesFlume 算例描述&#xff1a; 两列反向的孤立波相互作用 学习目标&#xff1a; 熟练掌握olaFlow的造波设置&#xff0c;波浪方向与消波方向设置 算例快照&#xff1a; 图1 两列反向孤立波相互作用文件结构&#xff1…

Linux Kernel 远程代码执行漏洞(CVE-2022-47939)

Linux Kernel 远程代码执行漏洞&#xff08;CVE-2022-47939&#xff09; CVE-2022-47939 据Security Affairs消息&#xff0c;近期披露的一个严重 Linux 内核漏洞会影响 SMB 服务器&#xff0c;可能导致远程代码执行。 Linux Kernel SMB2_TREE_DISCONNECT 命令处理中存在远程…

C进阶:征服指针之指针与数组强化笔试题练习(2)

&#x1f432;&#x1f996; 本篇文章是接上篇文章的&#xff0c;上篇文章链接&#xff1a;http://t.csdn.cn/RogqL 目录 &#x1f407;&#x1f431;一.关于 strlen 函数与数组、指针的综合笔试题 &#x1f638;T1. &#x1f406; T2. &#x1f405;T3. &#x1f432;一.关…

网络和通信安全中的SSL/TLS国密改造

2021年3月&#xff0c;国家市场监管总局、国家标准化管理委员会发布公告&#xff0c;国家密码应用与安全性评估的关键标准GB/T39786—2021《信息安全技术信息系统密码应用基本要求》&#xff08;以下简称“GB/T39786”&#xff09;正式发布&#xff0c;GB/T 39786是贯彻落实《中…

CSS浮动与CSS定位装饰 Day3

结构伪类选择器 结构伪类 公式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>伪类</title><style>li:nth-child(4n){background-color: red;}</style> </head> <body>…