将服务器select模型设置为非阻塞,处理更多业务

news2024/11/14 21:16:35

timeval结构体在头文件为sys/time.h中,定义如下:

struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* and microseconds */
};

其中tv_sec是秒,tv_usec是微秒(microsecond ),即10-6秒 

》》阻塞和非阻塞
​1、阻塞就是:当某个事件或者任务在执行过程中,它发出一个请求操作,但是由于该请求操作需要的条件不满足,那么就会一直在那等待,直至条件满足;

2、非阻塞就是:当某个事件或者任务在执行过程中,它发出一个请求操作,如果该请求操作需要的条件不满足,会立即返回一个标志信息告知条件不满足,不会一直在那等待。

    这就是阻塞和非阻塞的区别。也就是说阻塞和非阻塞的区别关键在于当发出请求一个操作时,如果条件不满足,是会一直等待还是返回一个标志信息。

​    客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和exceptfds(异常)。会定时对IO进程查询,没有数据会立即返回。select会阻塞住监视3类文件描述符,等有数据、可读、可写、出异常 或超时、就会返回;返回后通过遍历fdset整个数组来找到就绪的描述符fd,进行对应的IO操作。但是select中的timeval设置成{0,0};就可以变为非阻塞的,服务器就可以处理更多的事务。

server.cpp

#define WIN32_LEAN_AND_MEAN //尽力减少一些其他依赖库的引用
#define _WINSOCK_DEPRECATED_NO_WARNINGS

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

enum CMD {
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};

struct DataHeader {
	short dataLength;//数据长度
	short cmd;
};

//DataPackage
struct Login:public DataHeader{
	Login() {
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char passWord[32];
};

struct LoginResult :public DataHeader {
	LoginResult() {
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RESULT;
		result = 0;
	}
	int result;
};

struct Logout :public DataHeader {
	Logout() {
		dataLength = sizeof(Logout);
		cmd = CMD_LOGOUT;
	}
	char userName[32];
};

struct LogoutResult :public DataHeader {
	LogoutResult() {
		dataLength = sizeof(LogoutResult);
		cmd = CMD_LOGOUT_RESULT;
		result = 0;
	}
	int result;
};

std::vector<SOCKET>g_clients;

int processor(SOCKET _cSock) {
	//缓冲区
	char szRecv[1024] = {};
	// 5 接收客户端的数据
	int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);
	DataHeader* header = (DataHeader*)szRecv;

	if (nLen <= 0) {
		printf("客户端已退出,任务结束...\n");
		return -1;
	}
	switch (header->cmd) {
	case CMD_LOGIN:
	{
		//因为已经先将header读取出来了,所以需要修改读取位置
		recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
		Login* login = (Login*)szRecv;
		printf("收到命令:CMD_LOGIN 数据长度:%d,userName = %s passWord = %s\n", login->dataLength, login->userName, login->passWord);
		//忽略判断用户密码是否正确的过程
		LoginResult ret;
		send(_cSock, (char*)&ret, sizeof(LoginResult), 0);
	}
	break;
	case CMD_LOGOUT:
	{
		recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);
		Logout* logout = (Logout*)szRecv;
		printf("收到命令:CMD_LOGOUT szRecv:%d ,userName = %s \n", logout->dataLength, logout->userName);

		//忽略判断用户密码是否正确的过程
		LogoutResult ret;
		send(_cSock, (char*)&ret, sizeof(LogoutResult), 0);
	}
	break;
	default:
	{
		DataHeader header = { 0,CMD_ERROR };
		send(_cSock, (char*)&header, sizeof(DataHeader), 0);
	}
	break;
	}
}
int main() {
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//----------------------
	
	// -- 用Socket API建立简易TCP服务端
	// 1 建立一个socket 套接字(地址族规范、新套接字的类型规范、要使用的协议 )
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	// 2 bind 绑定用于接受客户端连接的网络端口
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);//host to net unsigned short
	_sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1");
	//bind(_sock, (sockaddr *)& _sin, sizeof(_sin));//​作用:给socket绑定端口号与具体位置
	if (SOCKET_ERROR == bind(_sock,(sockaddr*)&_sin,sizeof(sockaddr_in))) {
		printf("错误,绑定网络端口失败...\n");
	}
	else {
		printf("绑定网络端口成功...\n");
	}

	// 3 listen 监听网络端口
	//listen(_sock, 5);
	if (SOCKET_ERROR == listen(_sock, 5)) {
		printf("错误,监听网络端口失败...\n");
	}
	else {
		printf("监听网络端口成功...\n");
	}

	while (true) {
		//伯克利 socket 
		fd_set fdRead;
		fd_set fdWrite;
		fd_set fdExp;

		//清理集合
		FD_ZERO(&fdRead);
		FD_ZERO(&fdWrite);
		FD_ZERO(&fdExp);

		//加入集合
		FD_SET(_sock, &fdRead);
		FD_SET(_sock, &fdWrite);
		FD_SET(_sock, &fdExp);

		
		for (int n = (int)g_clients.size() - 1; n >= 0; n--)
		{
			//判断集合是否在集合中
			FD_SET(g_clients[n], &fdRead);
		}

		//nfds 是一个整数值 是指fd_set集合中所有描述符(socket)的范围,而不是数量
		//既是所有文件描述符最大值+1,在Windwos中这个参数可以写0
		timeval t = { 0,0 };
		int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp,&t);
		if (ret < 0) {
			printf("select任务结束...\n");
			break;
		}

		if (FD_ISSET(_sock, &fdRead)) {
			FD_CLR(_sock, &fdRead);
			// 4 accept 等待接受客户端连接
			sockaddr_in clientAddr = {};
			int nAddrLen = sizeof(sockaddr_in);
			SOCKET _cSock = INVALID_SOCKET;

			_cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);
			if (INVALID_SOCKET == _cSock) {
				printf("错误,接收到无效客户端SOCKET...\n");
			}
			g_clients.push_back(_cSock);
			printf("新客户端加入:socket = %d, IP = %s \n", (int)_cSock, inet_ntoa(clientAddr.sin_addr));
		}
		for (int n = 0; n < fdRead.fd_count; n++)
		{
			if (-1 == processor(fdRead.fd_array[n])) {
				auto iter = find(g_clients .begin(), g_clients.end(), fdRead.fd_array[n]);
				if (iter != g_clients.end()) {
					g_clients.erase(iter);
				}
			}
		}
		
	}

	for (int n = (int)g_clients.size() - 1; n >= 0; n--)
	{
		closesocket(g_clients[n]);
	}

	// 6 关闭套接字closesocket 
	closesocket(_sock);
	//----------------------

	//清除Windows socket环境
	WSACleanup();
	printf("已退出...\n");
	getchar();
	return 0;
}

//严重性	代码	说明	项目	文件	行	禁止显示状态
//错误	C4996	'inet_ntoa': Use inet_ntop() or InetNtop() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings	EasyTcpServer	E : \net_c++\EasyTcpServer\server.cpp	51
//解决方案:#define _WINSOCK_DEPRECATED_NO_WARNINGS

client.cpp

#define WIN32_LEAN_AND_MEAN //尽力减少一些其他依赖库的引用
//#define _WINSOCK_DEPRECATED_NO_WARNINGS

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

/*
	网络数据报文的格式定义
	报文有两个部分,包头和包体
	包头:描述本次消息包的大小,描述数据的作用
	包体:数据
*/

enum CMD {
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};

struct DataHeader {
	short dataLength;//数据长度
	short cmd;
};

//DataPackage
struct Login :public DataHeader {
	Login() {
		dataLength = sizeof(Login);
		cmd = CMD_LOGIN;
	}
	char userName[32];
	char passWord[32];
};

struct LoginResult :public DataHeader {
	LoginResult() {
		dataLength = sizeof(LoginResult);
		cmd = CMD_LOGIN_RESULT;
		result = 0;
	}
	int result;
};

struct Logout :public DataHeader {
	Logout() {
		dataLength = sizeof(Logout);
		cmd = CMD_LOGOUT;
	}
	char userName[32];
};

struct LogoutResult :public DataHeader {
	LogoutResult() {
		dataLength = sizeof(LogoutResult);
		cmd = CMD_LOGOUT_RESULT;
		result = 0;
	}
	int result;
};

int main() {
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//----------------------
	
	//-- 用Socket API建立简易TCP客户端
	// 1 建立一个Socket
	SOCKET _sock = socket(AF_INET,SOCK_STREAM,0);
	if (INVALID_SOCKET == _sock) {
		printf("错误,建立Socket失败...\n");
	}
	else {
		printf("建立Socket成功...\n");
	}
	// 2 连接服务器 connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
	if (SOCKET_ERROR == ret) {
		printf("错误,连接服务器失败...\n");
	}
	else {
		printf("连接服务器成功...\n");
	}

	while (true) {
		//3 输入请求命令
		char cmdBuf[128] = {};
		scanf("%s",cmdBuf);
		//4 处理请求命令
		if (0 == strcmp(cmdBuf, "exit")) {
			printf("收到exit命令,任务结束...\n");
			break;
		}
		else if(0 == strcmp(cmdBuf,"login")) {
			Login login;
			strcpy(login.userName, "呵呵哒!");
			strcpy(login.passWord, "123456");
			//5 向服务器发送请求命令
			send(_sock, (const char*)&login, sizeof(Login), 0);
			
			//接收服务器返回的数据
			LoginResult loginResult = {};
			recv(_sock,(char *)&loginResult,sizeof(LoginResult),0);
			printf("loginResult:%d \n", loginResult.result);
		}
		else if (0 == strcmp(cmdBuf, "logout")) {
			Logout logout;
			strcpy(logout.userName, "呵呵哒!");
			//5 向服务器发送请求命令
			send(_sock, (const char*)&logout, sizeof(Logout), 0);

			//接收服务器返回的数据
			LogoutResult logoutResult = {};
			recv(_sock, (char*)&logoutResult, sizeof(LogoutResult), 0);
			printf("logoutResult:%d \n", logoutResult.result);
		}
		else {
			printf("不支持的命令,请您重新输入...\n");
		}
	}
	
	// 4关闭套接字 closesocket
	closesocket(_sock);
	
   //---------------------- 
	//清除Windows socket环境
	WSACleanup();
	printf("已退出...");
	getchar();
	return 0;
}

//严重性	代码	说明	项目	文件	行	禁止显示状态
//错误	C4996	'inet_addr': Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings	EasyTcpClient	E : \net_c++\EasyTcpClient\client.cpp	26

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

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

相关文章

[单片机框架][bsp层][cx32l003][bsp_tim] Baes TIM 基础定时器配置和使用

文章目录 一、基础定时器介绍二、功能描述(1) Buzzer 功能 三、示例代码(PWM) 一、基础定时器介绍 基础定时器 Base Timer 包含两个定时器 TIM10/11。TIM10/11 功能完全相同。TIM10/11 是同步定时/计数器&#xff0c;可以作为 16/32 位自动重装载功能的定时/计数器&#xff0c…

VS2022配置GDAL

GDAL&#xff08;Geospatial Data Abstraction Library&#xff09;是一个用于处理地理空间数据的开源库。它提供了一组功能丰富的API&#xff0c;用于读取、写入、转换和处理各种地理空间数据格式&#xff0c;包括栅格数据&#xff08;如卫星图像、数字高程模型&#xff09;和…

Jupyter创建Anaconda多个虚拟环境教程

这里写目录标题 1.1界面化创建虚拟环境1.2命令行创建虚拟环境2.查看是否创建成功3.激活虚拟环境pylessonppt4.更改工作目录5.删除6.查看是否删除成功 1.1界面化创建虚拟环境 1.2命令行创建虚拟环境 conda create -n myenv——name pythonx.xmyenv-name:自己定义的环境名称 pyt…

fastjson反序列化漏洞复现

fastjson反序列化漏洞复现 一.影响版本: Fastjson<1.2.24二.实验过程图三.实验步骤四&#xff0c;实验结果以及参考链接 一.影响版本: Fastjson<1.2.24 二.实验过程图 (踩坑) rmijndi环境&#xff1a;java.sql.SQLException: JdbcRowSet (连接) JNDI 无法连接 2、ldapjn…

上海无纺布制造商【盈兹】申请纳斯达克IPO上市,募资1100万美元

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;来自上海的无纺布制造商【盈兹】&#xff0c;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&#xff08;ETZ&#…

Invalid bound statement (not found)的原因以及解决方法

相信我们在学习Mybatis的时候都出现过 Invalid bound statement (not found) 这个错误&#xff0c;一般由以下几种可能导致这个错误 一&#xff1a;mapper方法名 和 mapper.xml id名不对应 例如&#xff1a; mapper&#xff1a; 对应的mapper.xml 这里建议小伙伴们下载一个插…

Linux中的YUM源仓库和NFS文件共享服务

这里写目录标题 一 、YUM仓库源的介绍和相关信息1.1yum相关介绍1.2 Linux系统各家厂商用的安装源1.3 yum下载方式 二 、 yum 仓库源的三种搭建2.1yum 配置本地源2.2创建ftp源2.3 配置http源2.4 配置yum在线源 三 、NFS的简介3.1 什么是NFS3.2 linux中要使用NFS需要下载的软件包…

User Diverse Preference Modeling by Multimodal Attentive Metric Learning

BACKGROUND 现有模型通常采用一个固定向量去表示用户偏好&#xff0c;在假设——特征向量每一个维度都代表了用户的一种特性或者一个方面&#xff0c;这种方式似乎不妥&#xff0c;因为用户对于不同物品的偏好是不一样的&#xff0c;例如因演员喜欢一部电影&#xff0c;而因特…

C++中的vector容器

文章目录 vector的介绍vector的使用vector的定义vector初始化vector iterator的使用vector空间增长问题vector增删改查vector迭代器失效问题 vector的介绍 vector是封装动态数组的顺序容器。   就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。这也就意味着我…

Java核心技术 卷1-总结-15

自己实现的hashCode方法应该与equals方法兼容 Java核心技术 卷1-总结-15 视图与包装器子范围不可修改的视图同步视图受查视图 并发线程状态新创建线程可运行线程被阻塞线程和等待线程被终止的线程 视图与包装器 子范围 可以为很多集合建立子范围&#xff08;subrange&#x…

机器学习——朴素贝叶斯

目录 一、贝叶斯 1.什么是贝叶斯 3.贝叶斯下的朴素贝叶斯 二、朴素贝叶斯 1.高斯朴素贝叶斯 2.伯努利朴素贝叶斯 3.多项式朴素贝叶斯 前言 在所有的机器学习分类算法中&#xff0c;朴素贝叶斯和其他绝大多数的分类算法都不同。对于大多数的分类算法&#xff0c;比如决策…

【业务数据分析】—— 用户留存分析(以挖掘Aha时刻为例)

目录 一、用户留存是什么 二、为什么要考虑用户留存 1、为什么要考虑用户留存&#xff1f; 2、影响用户留存的可能因素 3、用户留存的3个阶段 三、怎么进行用户留存分析(挖掘Aha时刻) 1、Aha时刻 2、Aha时刻的作用 3、挖掘Aha时刻 一、用户留存是什么 在互联网行业中…

Three——二、加强对三维空间的认识

Three——二、加强对三维空间的认识 接上个例子我们接着往下看 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小&#xff0c;你可以根据需要改变尺寸。 使用方法&#xff1a; // AxesHelper&#xff1a;辅助观察的坐标系 const axesHelper new THRE…

Jetson nano B01学习笔记 -- 系统环境配置以及ROS安装

文章目录 一、Jetson nano 简介二、 系统环境配置1、系统镜像烧录2、CUDA环境配置 三、 ROS安装和环境配置总结 一、Jetson nano 简介 Jetson Nano是一款体积小巧、功能强大的人工智能嵌入式开发板&#xff0c;于2019年3月由英伟达推出。它预装Ubuntu 18.04LTS系统&#xff0c;…

有什么好用的远程工具吗

沟通在任何类型的工作中都扮演着重要的角色。但当谈到远程工作时&#xff0c;这一点就更为重要。因此&#xff0c;您的组织必须找到可以让您的团队保持一致的工具。 在某些方面&#xff0c;项目管理扮演着类似的角色。 您会注意到&#xff0c;下面的大多数工具都会直接影响您的…

Android进阶宝典—Koin使用和原理分析

一、理解设计模式 控制反转 是面向对象编程中的一种设计原则&#xff0c;可以用来减低计算机代码之间的耦合度。 实现控制反转最常见的方式叫做依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;&#xff0c;依赖注入(Dependency Injection)和控制反…

提高硬件设计能力的学习路线

不懂硬件的人&#xff0c;会觉得硬件高深莫测&#xff0c;“为什么他改几个电阻、电容就调出来&#xff0c;我弄个半天没搞定&#xff1f;”&#xff0c;“噢&#xff0c;靠的是经验”&#xff0c;但是经验又是什么呢&#xff1f;不能形容&#xff0c;反正就是不明觉厉。 就是…

Git(版本控制:前端git使用全流程)

1.版本控制&#xff08;简单理解&#xff1a;就是软件对每次操作之后提交的记录&#xff09; 文件版本 版本控制软件 版本控制的好处 版本控制的分类 本地版本控制系统 集中化版本控制系统 分布式版本控制系统 2. Git基础概念与工作流程 什么是Git&#xff1f; 开源的…

单片机编程中的裸机编程和多任务系统FreeRTOS系统详解,以及怎么学习FreeRTOS,看哪家的教程?(合集)

单片机编程中的裸机系统和多任务系统 学习了那么久的stm32还停留在裸机&#xff1f;&#xff1f;&#xff1f; 单片机编程中的裸机系统和多任务系统.1 裸机系统1.1轮询系统1.2 前后台系统 2 多任务操作系统3 为什么要学习多任务操作系统&#xff1f;&#xff1f;4 怎么学习Free…

诊断CAPL自动化(6) —— 诊断自动化测试,实战演示

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe,博客目录大全,点击跳转👉 下图是UDS协议测试的部分测试用例,该表格在文章末尾网盘自己获取经过前面几…