网络编程 select模型

news2024/11/24 16:55:18

       

目录

select模型详解

        select函数解释

整体代码


        select模型在代码上和c/s模型的前面一部分是一样的,可以去看 这个icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_62859191/article/details/128397927?spm=1001.2014.3001.5501,相同的代码如下

#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  wdSockMsg;
	int nRes = WSAStartup(wdVersion, &wdSockMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
	}
	if (wdSockMsg.wVersion != HIBYTE(2) || wdSockMsg.wVersion != LOBYTE(2))
	{
		WSACleanup();
		printf("版本不对\n");
		return 0;
	}

	//第二步 创建socket
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (socketServer == INVALID_SOCKET)
	{
		int a = WSAGetLastError();//获取错误码
		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 sockaddr*)&si, sizeof(si)))
	{
		int a = WSAGetLastError();
		closesocket(socketServer);
		WSACleanup();
		printf("创建socket出错\n");
		return 0;
	}

	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		int a = WSAGetLastError();
		closesocket(socketServer);
		WSACleanup();
		printf("监听出错\n");
		return 0;
	}


	closesocket(socketServer);
	WSACleanup();
	return 0;
}

select模型详解

        作用:select模型使用来解决c/s模型中收发消息时阻塞的问题,用在服务器上

        需要用到 fd_set 网络中定义好的结构体,结构体原型

typedef struct fd_set {
  u_int  fd_count;
  SOCKET fd_array[FD_SETSIZE];
} fd_set, FD_SET, *PFD_SET, *LPFD_SET;

系统也给 fd_set 结构体提供了4个操作宏

FD_ZERO:把fd_set结构体中的所有数据清零

FD_SET:在fd_set结构体中添加一个参数

FD_CLR:删除指定的socket变量

FD_ISSET:判断fd_set结构体中是否有指定的socket变量,有返回非0,没有返回0

        ##代码样例

//创建一个存放socket的结构体
	fd_set clientSockets;
	FD_ZERO(&clientSockets);//把集合中的所有数据清零
	FD_SET(socketServer, &clientSockets);//添加元素进集合
	//删除的时候一定要close
	FD_CLR(socketServer, &clientSockets);//删除集合中指定的元素
	//closesocket(socketServer);
	int a = FD_ISSET(socketServer, &clientSockets);//判断集合中是否有指定的元素 存在返回非0,不在返回0

        select函数解释

该函数确定一个或多个套接字的状态(如有必要)等待执行同步 I/O。函数原型

int WSAAPI select(
  [in]      int           nfds,
  [in, out] fd_set        *readfds,
  [in, out] fd_set        *writefds,
  [in, out] fd_set        *exceptfds,
  [in]      const timeval *timeout
);

参数1 nfds:填0,表示可以兼容旧版本

参数2 readfds:检查是否有可读的socket,即客户端发来消息,对应的socket就会被设置

参数3 writefds:检查是否有可写的socket,就是可以给哪些客户端套接字发消息,只要链接成功建立起来了,那该客户端套接字就是可写的。

参数4 exceptfds:检查套接字上的异常错误,用法跟2,3一样

参数5 timeout:这是一个结构体对象,结构体原型如下,该参数可以设置最大等待时间,结构体中的两个成员,tv_sec表示秒,tv_usec表示微妙,设置为00时,非阻塞状态,立刻返回,设置为1,1表示在无客户端响应下等待1秒1微妙,如果填入NULL,会完全阻塞,直到客户端有响应

typedef struct timeval {
  long tv_sec;
  long tv_usec;
} TIMEVAL, *PTIMEVAL, *LPTIMEVAL;

        返回值:该函数如果返回0,表示在客户端等待时间内没有反应,如果返回大于0的数,表示有客户端请求交流,如果返回 SOCKET_ERROR,表示发生了错误,使用WSAGetLastError()可以获取错误码

        在使用参数4时,需要用到函数 getsockopt ,该函数可以获取到socket发生错误时的错误信息,函数原型,及使用样例

int WSAAPI getsockopt(
  [in]      SOCKET s,
  [in]      int    level,
  [in]      int    optname,
  [out]     char   *optval,
  [in, out] int    *optlen
);
//使用样例
char str[100] = {0};
int len = 99;
getsockopt(errorSockets.fd_array[i], SOL_SOCKET, SO_ERROR, str, &len)

        错误信息返回会给到字符数组str,如果可以找到错误,返回0,否则返回SOCKET_ERROR

整体代码

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

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


int main()
{
	printf("**********服务器***********\n");
	//第一步 打开网络库/校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA  wdSockMsg;
	int nRes = WSAStartup(wdVersion, &wdSockMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
	}
	if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))
	{
		WSACleanup();
		printf("版本不对\n");
		return 0;
	}

	//第二步 创建socket
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (socketServer == INVALID_SOCKET)
	{
		int a = WSAGetLastError();//获取错误码
		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 sockaddr*)&si, sizeof(si)))
	{
		int a = WSAGetLastError();
		closesocket(socketServer);
		WSACleanup();
		printf("创建socket出错\n");
		return 0;
	}

	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		int a = WSAGetLastError();
		closesocket(socketServer);
		WSACleanup();
		printf("监听出错\n");
		return 0;
	}

	//创建一个存放socket的结构体
	fd_set allSockets;
	FD_ZERO(&allSockets);//把集合中的所有数据清零
	//把服务器装进去
	FD_SET(socketServer, &allSockets);//添加元素进集合

	删除的时候一定要close
	//FD_CLR(socketServer, &clientSockets);//删除集合中指定的元素
	closesocket(socketServer);
	//int a = FD_ISSET(socketServer, &clientSockets);//判断集合中是否有指定的元素 存在返回非0,不在返回0

	while (1)
	{
		fd_set readSockets = allSockets;//第二个参数的中间变量
		fd_set writeSockets = allSockets;//第三个参数的中间变量
		fd_set errorSockets = allSockets;//第四个参数的中间变量
		FD_CLR(socketServer, &writeSockets);
		FD_CLR(socketServer, &errorSockets);
		struct timeval st;
		st.tv_sec = 3;
		st.tv_usec = 0;
		int sRes = select(0, &readSockets, &writeSockets, NULL, &st);//该函数会改变tempSockets中的元素

		if (sRes == 0)
		{
			//客户端没有响应
			continue;
		}
		else if (sRes > 0)
		{
			//错误socket处理
			for (u_int i = 0; i < errorSockets.fd_count; i++)
			{
				char str[100] = { 0 };
				int len = 99;
				//参数4:通过参数2进行参数传址获取错误码
				if (SOCKET_ERROR == getsockopt(errorSockets.fd_array[i], SOL_SOCKET, SO_ERROR, str, &len))
				{
					printf("无法得到错误信息\n");
				}
			}

			//服务器发送消息
			for (u_int i = 0; i < writeSockets.fd_count; i++)
			{
				if (SOCKET_ERROR == send(writeSockets.fd_array[i], "OK", 2, 0))//客户端正常关闭也时返回SOCKET_ERROR
				{
					printf("发送出错\n");
					int a = WSAGetLastError();//获取错误码
				}
			}

			//客户端有发消息
			for (u_int i = 0; i < readSockets.fd_count; i++)
			{
				if (readSockets.fd_array[i] == socketServer)//表示有客户端请求连接
				{
					//创建客户端socket
					SOCKET socketClient = accept(socketServer, NULL, NULL);
					if (socketClient == INVALID_SOCKET)
					{
						//链接出错
						continue;
					}
					//否则把该socket放入tempSocket数组中
					FD_SET(socketClient, &allSockets);
					printf("客户端连接成功\n");
				}
				else//否则就是有客户端发送消息,需要接收消息
				{
					char buf[1500] = { 0 };
					int nRecv = recv(readSockets.fd_array[i], buf, 1499, 0);
					if (nRecv == 0)//表示客户端下线
					{
						SOCKET socketTemp = readSockets.fd_array[i];
						printf("客户端下线\n");
						FD_CLR(readSockets.fd_array[i], &allSockets);
						closesocket(socketTemp);
					}
					else if (nRecv > 0)//接收到客户端发来的消息
					{
						printf("%d	%s\n", nRecv, buf);
					}
					else//SOCKET_ERROR 非正常关闭
					{
						SOCKET socketTemp = readSockets.fd_array[i];
						printf("客户端被迫退出\n");
						int a = WSAGetLastError();//获取错误码
						FD_CLR(readSockets.fd_array[i], &allSockets);
						closesocket(socketTemp);

					}
				}
			}
		}
		else
		{
			//发生错误
			break;
		}
	}
	//释放所有的socket
	for (u_int i = 0; i < allSockets.fd_count; i++)
	{
		closesocket(allSockets.fd_array[i]);
	}
	closesocket(socketServer);
	WSACleanup();
	return 0;
}

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

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

相关文章

cadence SPB17.4 - orcad - WARNING(ORCAP-2354) - Wire is hanging at Point

文章目录cadence SPB17.4 - orcad - WARNING(ORCAP-2354) - Wire is hanging at Point概述普通画法, 引起的不可理解的hang wire 警告ENDcadence SPB17.4 - orcad - WARNING(ORCAP-2354) - Wire is hanging at Point 概述 在使用SPB17.4从一个PCB中反推原理图. 原理图重建的差…

Jenkins入门(一)Jenkins介绍、GitLab基础环境安装

视频学习地址&#xff1a;01-Jenkins教程简介_哔哩哔哩_bilibili 一、介绍&#xff1a; Jenkins是一个独立的开源自动化服务器&#xff0c;可用于自动化各种任务&#xff0c;如构建&#xff0c;测试和部署软件。 它替代了管理员手动集成、构建、测试&#xff0c;提交代码后自…

深度学习:ResNet从理论到代码

深度学习&#xff1a;ResNet从理论到代码面临的问题模型退化问题ResNet核心思想反向传播公式推导残差的由来残差模块为什么效果好代码实现面临的问题 模型退化问题 随着网络层数加深&#xff0c;性能逐渐降低&#xff0c;但它并不是过拟合&#xff0c;因为在test error降低的同…

多准则决策问题评估方法 | 灰云模型(含代码)

目前多准则决策问题的评估方法主要分为定性分析方法和定量分析方法两类。定性分析方法主要包括专家咨询、熵权法、案例研究和德尔菲法等&#xff1b;定量分析法主要包括层次分析法、主成分分析法、因子分析法、模糊综合评价法、灰色综合评价法以及数据包络分析法&#xff08;DE…

Apollo星火计划学习笔记——Apollo路径规划算法原理与实践

文章目录1. 路径规划算法总体介绍1.1 Task&#xff1a; LANE_CHANGE_DECIDER1.2 Task&#xff1a; PATH_REUSE_DECIDER1.3 Task&#xff1a; PATH_BORROW_DECIDER1.4 Task&#xff1a; PATH_BOUNDS_DECIDER1.5 Task&#xff1a; PIECEWISE_JERK_PATH_OPTIMIZER1.6 Task&#xf…

人脸识别经典论文Arcface解读

来源&#xff1a;投稿 作者&#xff1a;小灰灰 编辑&#xff1a;学姐 研究背景 1、在人脸识别时&#xff0c;我们需要特征的discrimination 2、之前提出到的一些方法&#xff0c;如triplet loss,center loss, L-softmax,a-softmax都有一些缺陷。 3、centerloss&#xff1a;提…

2022.12.25 学习周报

文章目录摘要文献阅读1.题目2.摘要3.问题和方案4.介绍5.Attention Transfer5.1 Activation-based Attention Transfer5.2 Gradient-based Attention Transfer6.实验7.结论深度学习Attention机制的本质Encoder to Decoder抛开encoder-decoderAttention函数工作机制Attention机制…

20221225 海豚调度2.0.5连接星环库使用记录

阳阳的一周&#xff0c;算是挺过来了&#xff0c;现在只剩感冒了&#xff0c;迷迷糊糊的干了一周&#xff0c;混口饭吃不容易呀&#xff01;简单记录一下遇到的问题吧&#xff01; 连接hive(星环)数据库失败 方案一 &#xff1a; 海豚调度2.0.5使用的hive包是2.0版本,星环库包…

云原生之部署wordpress博客及设置圣诞主题风格

2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ CSDN诚邀各位技术er分享关于圣诞节的各种技术创意&#xff0c;展现你与众不同的精彩&#xff01;参与本次投稿即可获得【话题达人】勋章【圣诞快乐】定制勋章&#xff08;1年1次&#xff0c;错过要等下一年喔&#…

Python的条条框框

Python的条条框框 了解编程语言的分类 从运行角度的分类 从运行角度来看&#xff0c;编程语言的类型可以分为两种&#xff1a;编译型和解释型。 Python属于解释型语言。 解释型语言&#xff1a; 代码可以直接运行。当然&#xff0c;这也是依赖于附加程序&#xff08;解释器&…

【VUE3】保姆级基础讲解(三)非父子组件通讯,$refs,动态组件,keep-alive,Composition API

目录 非父子组件通讯 全局事件总线mitt库 组件的生命周期 $refs 动态组件 keep-alive 异步打包 v-model绑定组件 Composition API 定义响应式数据 readonly toRefs与toRef computed $ref 生命周期钩子 provide和inject watch侦听 watchEffect script setup语法…

C++必须掌握的知识点

面向对象的三大特性 封装 继承 父类中所有的非静态成员都会被子类继承下去&#xff0c;只是父类的私有成员被编译器屏蔽了&#xff0c;访问不到。可以利用开发人员工具查看对象模型继承中&#xff0c;先构造父类&#xff0c;再构造子类&#xff0c;析构的顺序和构造的顺序完…

QT系列第8节 自定义对话框

在实际业务开发中经常要有各种各样的对话框来处理用户信息&#xff0c;本节就结合例子来说明如何自定义对话框。 目录 1.创建对话框 2.创建非模态对话框 3.创建模态对话框 4.综合案例 1.创建对话框 &#xff08;1&#xff09;项目鼠标右键菜单 - 添加新文件 &#xff08;…

Hexo + Butterfly 自定义页脚

原文链接 &#xff1a;Hexo Butterfly 自定义页脚 推荐阅读 基于 Hexo 从零开始搭建个人博客&#xff08;一&#xff09;: 环境准备基于 Hexo 从零开始搭建个人博客&#xff08;二&#xff09;: 项目初识基于 Hexo 从零开始搭建个人博客&#xff08;三&#xff09;: 主题安装…

CSDN每日一练最长递增的区间长度 C语言

题目名称&#xff1a;最长递增的区间长度 时间限制&#xff1a;1000ms 内存限制&#xff1a;256M 题目描述 给一个无序数组&#xff0c;求最长递增的区间长度。如&#xff1a;[5,2,3,8,1,9] 最长区间 2,3,8 长度为 3 &#xff08;注意&#xff1a;测试用例仅做参考&#xff0c;…

Spring web开发之Request 获取三种方式

在开发 Java Web 项目中&#xff0c;我们经常使用 HttpServletRequest 获取请求参数、请求头等信息。在Spring项目&#xff0c;我们通常会使用 Spring 提供的注解获取参数&#xff0c;如 RequestParam、RequestHeader。 不过在某些场景下&#xff0c;我们可能需要从 HttpServl…

初识Docker:(4)Docker基本操作

初识Docker&#xff1a;&#xff08;4&#xff09;Docker基本操作1 镜像操作1.1 镜像名称1.2 镜像操作命令1.3 案例&#xff1a;docker拉取nginx镜像利用docker save将nginx镜像导出磁盘&#xff0c;然后再通过load加载回来1.4 镜像操作总结2 容器操作2.1 案例创建运行一个ngin…

【阅读笔记】《持续交付2.0》中理解分支、发布策略

文章目录1. 前言1.1 分支、发布 管理上解耦2. 主干 (Trunk) 和分支 (Branch)2.1 Trunk 开发 Trunk 发布2.1.1 Trunk 开发 Trunk 发布需要解决&#xff1a;重构的需求2.1.2 Trunk 开发 Trunk 发布需要解决&#xff1a;未开发完成的功能被带入发布版本2.2 Trunk 开发 Branch 发布…

leetcode:6272. 好分区的数目【思维转换(正难则反) + dp定义 + 背包问题 + 选or不选】

目录题目截图题目分析ac code总结题目截图 题目分析 先特判&#xff0c;如果sum(nums) < 2 * k显然不可能成功&#xff01;返回0出现Mod大概率就是dp1000的话提示我们用平方复杂度的dp这种取子序列的问题&#xff0c;本质就是选or不选的问题如果我们只考虑一维dp,dp[i]肯定…

Linux--信号

目录1. 信号概念2. 信号产生前2.1 信号产生的各种方式3. 信号产生中信号保存的方式3.1 阻塞信号3.2 信号屏蔽字4. 信号产生后信号处理的方式4.1 信号集操作函数4.2 sigprocmask函数4.3 sigpending函数4.4 sigaction函数5. 信号是什么时候被处理的1. 信号概念 信号是进程之间事…