网络编程第五天

IO多路复用实现TCP服务器和客户端

运行结果:

select实现TCP服务器:

#include <myhead.h>

#define PORT 8888 				//1024~49151
#define IP "192.168.170.126" 	//ifconfig本机IP

int deal_cli_connect(int sfd,struct sockaddr_in *pcin , fd_set *preadfds, int *pmaxfd);
int deal_cli_msg(int i, fd_set *preadfds, int *pmaxfd, struct sockaddr_in *pcin);

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success sfd = %d __%d__\n",sfd,__LINE__);

	//允许端口快速被复用---监测端口号是否真的有进程在占用,如果没有,则快速复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速复用成功\n");


	//定义并填充地址消息结构体,给服务绑定使用
	//真实的地址信息结构体根据地址族指定,AT_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET; 		//必须为AF_INET
	sin.sin_port 		= htons(PORT); 	//端口号的网络字节序, 1024~49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP的网络字节序

	//绑定服务器自身的地址消息 -->必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success __%d__\n",__LINE__);

	//将套接字设置为被动监听状态
	if(listen(sfd,128) < 0)
	{
		ERR_MSG("lesten");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	struct sockaddr_in savecin[1024 - 3]; 	//保存客户端的信息

	//先创建一个集合
	//fd_set本质上是一个结构体,内部仅有一个long类型的数组成员
	//readfds若不初始化,则可能会随机到一个有效但是不想让内核监测的文件描述符
	//所以readfds必须清空
	fd_set readfds, tempfds;
	FD_ZERO(&readfds);//清空集合

	//将需要监测的文件描述符填充到读集合中
	FD_SET(0, &readfds); 	//将0号添加到集合中
	FD_SET(sfd, &readfds); 	//将sfd添加到集合中
	int s_res;
	int newfd = -1, maxfd = sfd;
	char buf[128];

	while(1)
	{
		tempfds = readfds;
		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res == 0)
		{
			//超时
			printf("time out...\n");
			return -1;
		}
		else if(s_res < 0)
		{
			//函数运行失败
			ERR_MSG("select");
			return -1;
		}

		/*
		 * 能运行到当前位置,则代表有文件描述符准备就绪
		 * 判断集合中哪个文件描述符准备就绪,走对应的处理函数即可
		 *
		 * 当select函数解除阻塞后,集合中只会剩下产生事件的文件描述符
		 * 例如:
		 * 0准备就绪,则集合中只会剩下0
		 * sfd准备就绪,则集合中只会剩下sfd
		 * 0和sfd准备就绪,则集合中会剩下0和sfd
		 * 所以,只需要判断集合中剩下哪个文件描述符,就可以知道哪个文件描述符准备就绪
		 * */
		for(int i = 0;i <= maxfd;i++)
		{
			if(FD_ISSET(i, &tempfds) == 0)
			{
				continue;
			}
			if(0 == i)
			{
				printf("键盘触发事件\n");
				fgets(buf, sizeof(buf), stdin);
				buf[strlen(buf) -1] = '\0';
				printf("buf = %s\n", buf);
			} 
			else if(sfd == i)
			{
				printf("触发客户端连接事件\n");
				deal_cli_connect(sfd, savecin, &readfds, &maxfd);
			}
			else
			{
				printf("触发客户端交互事件\n");
				deal_cli_msg(i, &readfds, &maxfd, savecin);

			}
		}

	}

	//关闭所有文件描述符
	close(newfd);
	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

int deal_cli_connect(int sfd,struct sockaddr_in *pcin , fd_set *preadfds, int *pmaxfd)
{
	struct sockaddr_in cin;
	socklen_t addrlen;
	//获取连接成功的客户端信息,生成一个新的套接字
	int newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
	if(newfd < 0)
	{
		ERR_MSG("accept");
		return -1;
	}
	//打印客户端的IP和端口
	printf("[%s : %d]newfd = %d 客户端连接成功__%d__\n",\
			inet_ntoa(cin.sin_addr) , ntohs(cin.sin_port),newfd,__LINE__);
	//添加到集合中
	FD_SET(newfd, preadfds);
	*pmaxfd = *pmaxfd > newfd ? *pmaxfd:newfd;

	//保存客户端的信息
	pcin[newfd-3] = cin;

	return 0;
}

int deal_cli_msg(int i, fd_set *preadfds, int *pmaxfd, struct sockaddr_in *pcin)
{
	char buf[128] = "";

	bzero(buf,sizeof(buf));
	//接收客户端数据
	ssize_t res = recv(i, buf, sizeof(buf), 0);
	if(res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if(0 == res)
	{
		printf("[%s : %d]newfd = %d 客户端下线__%d__\n",\
				inet_ntoa(pcin[i-3].sin_addr) , ntohs(pcin[i-3].sin_port), i,__LINE__);
		while(FD_ISSET(*pmaxfd, preadfds) == 0 && (*pmaxfd)-- > 0);
		close(i);
		FD_CLR(i, preadfds);
		return 0;
	}
	printf("[%s : %d]newfd = %d : %s__%d__\n",\
			inet_ntoa(pcin[i-3].sin_addr) , ntohs(pcin[i-3].sin_port), i, buf,__LINE__);

	//向客户端发送数据 ---> //从终端获取数据
	//bzero(buf,sizeof(buf));
	strcat(buf,"^_^");
	//printf("请输入>>>");
	//fgets(buf,sizeof(buf),stdin);
	//buf[strlen(buf)-1] = '\0';

	if(send(i, buf, sizeof(buf), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	printf("send success __%d__\n", __LINE__);
	
	return 0;
}

select实现TCP客户端:

#include <myhead.h>

#define PORT 8888 				//服务器的端口
#define IP "192.168.124.81" 	//服务器的IP

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success cfd = %d __%d__\n",cfd,__LINE__);

	//定义并填充地址消息结构体,给服务绑定使用
	//真实的地址信息结构体根据地址族指定,AT_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET; 		//必须为AF_INET
	sin.sin_port 		= htons(PORT); 	//服务器绑定的端口
	sin.sin_addr.s_addr = inet_addr(IP);//服务器绑定的IP

	//绑定客户端自身的地址消息 -->非必须绑定
	//若客户端没有绑定,则操作系统会自动绑定本机IP和随机端口(49152~65535)

	//连接到指定的服务器
	if(connect(cfd ,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
		return -1;
	}
	printf("connect success __%d__\n",__LINE__);

	//接收缓冲
	char buf[128] = "";

	//设置监听的集合
	fd_set readfds, tempfds;
	//集合清空
	FD_ZERO(&readfds);
	FD_SET(0, &readfds);
	FD_SET(cfd, &readfds);
	int s_res;
	
	ssize_t res = 0;
	while(1)
	{
		tempfds = readfds;
		s_res = select(cfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if( 0 == s_res)
		{
			printf("time out...\n");
			return -1;
		}

		if(FD_ISSET(0, &tempfds))
		{
			//向服务器发送数据 ---> //从终端获取数据
			bzero(buf,sizeof(buf));

			//printf("请输入>>>");
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = '\0';

			if(send(cfd, buf, sizeof(buf), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
			printf("send success __%d__\n", __LINE__);bzero(buf,sizeof(buf));
		}

		if(FD_ISSET(cfd, &tempfds))
		{
			bzero(buf,sizeof(buf));
			//接收服务器数据
			res = recv(cfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(0 == res)
			{
				printf("[%s : %d] 服务器下线__%d__\n",\
						inet_ntoa(sin.sin_addr) , ntohs(sin.sin_port),__LINE__);
				break;
			}
			printf("[%s : %d] : %s__%d__\n",\
					inet_ntoa(sin.sin_addr) , ntohs(sin.sin_port), buf,__LINE__);
		}
	}
	//关闭所有文件描述符

	if(close(cfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【产品经理】Axure原型工具教程

笔记为项目总结笔记&#xff0c;若有错误欢迎指出哟~ Axure原型工具教程 Axure简介原型图分类常用操作常用原件常用交互母版常用设备分辨率 Axure简介 Axure是一款专业的原型设计与交互设计软件&#xff0c;可以帮助用户快速创建高保真的原型和交互设计。Axure支持多种常见的交…

添加调试日志,bug消失

参考&#xff1a;就删了个printf&#xff0c;代码崩了&#xff01; 1、运行报错代码 #include "stdio.h" #include "stdlib.h" #include "string.h"void func1() {int arr[10];memset(arr, 1, sizeof(arr)); }void func2() {int index;int* ar…

ArcGIS渔网的多种用法

在ArcGIS中有一个渔网工具&#xff0c;顾名思义&#xff0c;可以用来创建包含由矩形像元所组成网络的要素类。不太起眼&#xff0c;但它的用途却有很多&#xff0c;今天跟大家分享一篇关于渔网的多种用途。 1.马赛克地图制作 2.基于网格的设施密度统计制作马赛克地图 准备材…

怎么使用jupter notebook并配置环境变量

有的时候需要使用Jupyter Notebook运行代码&#xff0c;Jupyter Notebook的主要特点&#xff1a; ① 编程时具有语法高亮、缩进、tab补全的功能。 ② 可直接通过浏览器运行代码&#xff0c;同时在代码块下方展示运行结果。 ③ 以富媒体格式展示计算结果。富媒体格式包括&…

抖店商品卡不出单做不起来?抖店玩法解读+经验分享,建议收藏

我是王路飞。 目前抖店最常见的出单玩法&#xff0c;一个是商品卡&#xff08;自然流量&#xff09;&#xff0c;一个是找达人带货。 新手喜欢商品卡的原因就是“流量免费”&#xff0c;但同样不止一个新手向我反映过&#xff0c;商品卡真的不好出单&#xff0c;还有风险&…

B055-Maven IDEA UML

目录 Maven简介安装与配置Maven常用命令Maven导包导入项目到eclipsepom.xml介绍查找jar包网址eclipse中使用maven命令&#xff1a; eclipse创建maven项目修改jdk版本eclipse创建maven web项目配置阿里云镜像仓库 IDEA简介安装idea创建java项目idea基本配置删除项目idea创建Mave…

十大数据分析工具排行榜出炉

成功的数据分析师不仅要具有一定的资格和教育&#xff0c;还必须精通一些特定的工具。尤其是在数据采集&#xff0c;数据清理&#xff0c;数据仓库&#xff0c;数据分析&#xff0c;以及数据可视化方面。 近几年&#xff0c;数据驱动对IT创新和企业业务发展都有好处&#xff0…

创新科技赋能,易点易动设备管理系统助力企业实现设备管理升级

在当今竞争激烈的商业环境中&#xff0c;企业对设备管理的要求越来越高。高效的设备管理不仅可以提高生产效率&#xff0c;降低成本&#xff0c;还可以确保设备安全和可靠性。然而&#xff0c;传统的手工管理方式已经无法满足企业快速发展的需求。为了解决这一问题&#xff0c;…

浅谈互联网架构演变

更好的阅读体验 \large{\color{red}{更好的阅读体验}} 更好的阅读体验 前言 可以将某个项目或产品的架构体系按照如下方式分层&#xff1a; 业务层面&#xff1a;项目业务体系技术层面&#xff1a; 数据架构&#xff1a;数据持久层策略应用架构&#xff1a;应用层的实现方式 …

阿里云日志表盘配置-图上展示想要的名称

一、背景 目前写出的一些表盘都是_col这种字段展示的&#xff0c;下次来看表盘的时候都不知道是什么意思了&#xff0c;所以想要将_col可视化一些&#xff0c;记录一下在这个阶段学到的知识。主要是阿里云日志在使用 AS起别名的时候会报错。 二、操作和遇到的注意问题点 我写…

基于openGauss5.0.0全密态数据库等值查询小案例

基于openGauss5.0.0全密态数据库等值查询小案例 一、全密态数据库简介二、环境说明三、测试步骤四、使用约束 一、全密态数据库简介 价值体现&#xff1a; 密态数据库意在解决数据全生命周期的隐私保护问题&#xff0c;使得系统无论在何种业务场景和环境下&#xff0c;数据在传…

解决Pycharm pip安装模块太慢问题,pycharm2022没有manage repositories配置镜像源

解决方案 方法清华阿里云中国科技大学华中理工大学 或者直接-i 加镜像 方法 URL写下面任意一个 清华 https://pypi.tuna.tsinghua.edu.cn/simple阿里云 http://mirrors.aliyun.com/pypi/simple/中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/华中理工大学 http:/…

postman高级用法

背景 在项目开发的过程中&#xff0c;少不了对接口的调用和对自己编写的接口进行自测&#xff0c;或者测试同学用来做接口测试用 问题 请求头&#xff1a;key:authenticationTokenvalue:获取token接口获取的token/external-data/guoyin-iot-platform-external-data-center/man…

如何解决mac无法访问github

确定github能访问的ip地址 点击检测按钮&#xff0c;找到比较快的ip 修改hosts文件&#xff1a;打开终端&#xff0c;输入 open /etc/hosts 后回车&#xff0c;打开mac的文本编辑器 add github.com 140.82.121.4 github.com 199.232.69.194 github.global.ssl.fastly.net …

【Image】GAN的超详细解释(以及奇怪的问题)

GAN原理 工作流程 下面是生成对抗网络&#xff08;GAN&#xff09;的基本工作原理 在GAN的架构中&#xff0c;有两个关键的组件&#xff1a;生成器&#xff08;Generator&#xff09;和鉴别器&#xff08;Discriminator&#xff09;。 生成器&#xff08;Generator&#xff0…

传感器基础:传感器使用与编程使用(六)

目录 常用传感器讲解二十--气体传感器&#xff08;KY-036&#xff09;具体讲解电路连接代码实现 常用传感器讲解二十一--加速度传感器&#xff08;ADXL335&#xff09;具体讲解电路连接代码实现 常用传感器讲解二十二--压力传感器&#xff08;BMP180&#xff09;具体讲解电路连…

nvm 的安装及使用 (Node版本管理器)

目录 1、nvm 介绍 2、nvm安装 3、nvm 使用 4、node官网可以查看node和npm对应版本 5、nvm安装指定版本node 6、安装cli脚手架 1、nvm 介绍 NVM 全称 node.js version management &#xff0c;专门针对 node 版本进行管理的工具&#xff0c;通过它可以安装和切换不同版本的…

Mysql之视图

Mysql之视图 常见的数据库对象视图概述为什么使用视图视图的理解创建视图创建单表视图别名的运用 创建多表联合视图利用视图对数据进行格式化contact 函数以视图为基&#xff0c;再创建新的视图 查看视图更新视图的数据一般情况不可更新的视图 修改和删除视图修改视图删除视图注…

SSL证书是什么?为什么需要SSL证书?

什么是SSL证书&#xff1f; SSL&#xff0c;全称为Secure Sockets Layer&#xff0c;是一种用于加密网络通信的协议。SSL证书是由一个可信赖的第三方机构颁发的数字证书&#xff0c;用于确保在用户与服务器之间传输的数据经过加密&#xff0c;保护用户隐私和信息安全。SSL证书…

flutter学习-day20-使用SafeArea组件处理各机型的安全距离

&#x1f4da; 目录 介绍分析示例和效果图特殊情况 1. 介绍 安全区域&#xff0c;指的是移动端设备的可视窗口范围。处于安全区域的内容不受圆角、刘海屏、iPhone 小黑条、状态栏等的影响&#xff0c;也就是说&#xff0c;我们要做好适配&#xff0c;必须保证页面可视、可操作…