网络编程 day3

思维导图        以select函数模型为例

思维导图2        对应 epoll模型 应使用的函数

题目

使用epoll函数实现 两个客户端 通过服务器 实现聊天

思路

在原先代码基础上

        实现 服务器 发向 客户端                使用客户端在服务器上的 套接字描述符 

        实现 客户端 接收 服务器消息        发向服务器的描述符 也可以用于读取服务器传来数据

实现通信

【1】客户端与服务器连接后,会被分配一个唯一的、不变的id号,直到本次连接断开前,此id号不会被改变或者销毁;

【2】服务器根据 收到的 PACK包中的 pack.type 参数,确定转发目标;        

【3】通过这个唯一id,服务器可以实现数据向 指定目标 的发送;

代码

服务器

struct PACK
{int size;	//数据包大小int type;	//对数据的操作char buf[1500];	//数据int conut;	//数据已经占用多少字节
};enum
{//以下参数适用于 client_id_ctl() 函数的 int cmd 参数insect_clinet=1,	//分配客户端id号 的指令get_client_id,		//获取客户端id号 的指令get_clinetfd,		//获取客户端描述符 的指令remove_clinet,		//移除客户端 的指令//以下参数适用于 数据包的 pack.type 值 send_to_server=-1,	//说明数据是发送向服务器的
};//客户端id号 操作函数//根据cmd不同,实现分配id 获取id 获取描述符 移除id等功能
int client_id_ctl(int arr[],int len,int cmd,int client);
//解析数据
void unlood(struct PACK *pack);int main(int argc, const char *argv[])
{int client_arr[10];	//用于存放 客户端在服务器 上的文件描述符memset(client_arr,-1,sizeof(client_arr));//创建服务器套接字int serverfd=socket(AF_INET,SOCK_STREAM,0);//创建并设置 "网络通信结构体"struct sockaddr_in addr;addr.sin_family=AF_INET;	//ipv4int port=atoi(argv[1]);addr.sin_port=htons(port);	//端口号 (大端存储)addr.sin_addr.s_addr=inet_addr("0.0.0.0");//套接字 绑定 ip 和 portint res=bind(serverfd,(struct sockaddr*)&addr,sizeof(addr));if(res==-1){printf("服务器 绑定ip和port 失败\n");perror("bind");return 0;}printf("服务器已就绪\n");//监听listen(serverfd,10);//创建动态的监视列表int epfd=epoll_create1(EPOLL_CLOEXEC);//设定 需要监视的描述符 以及 激活形式struct epoll_event event_serverfd={.events=EPOLLIN,.data.fd=serverfd};struct epoll_event event_stdinfd={.events=EPOLLIN,.data.fd=0};//添加到 [监视列表]epoll_ctl(epfd,EPOLL_CTL_ADD,serverfd,&event_serverfd);epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event_stdinfd);while(1){struct epoll_event active_list[10];    //用于存放被激活的描述符//开始监视 [监视列表]中描述符int len=epoll_wait(epfd,active_list,10,-1);    //被激活的描述符 在 active_list[10]数组中//遍历被激活的描述符for(int i=0;i<len;i++){int active_fd=active_list[i].data.fd;   //获取被激活的描述符fd//如果是服务器可读 说明有用户尝试连接if(active_fd==serverfd){//接收客户端连接int clientfd=accept(serverfd,NULL,NULL);//为客户端 分配id号int id=client_id_ctl(client_arr,10,insect_clinet,clientfd);// //将客户端 添加到 [监视列表]struct epoll_event event_client={.events=EPOLLIN,.data.fd=clientfd};epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&event_client);printf("服务器提示: 有新的客户端连接到服务器 分配id号:%d\n\n",id);}//如果数据来自 终端else if(active_fd==0){printf("服务器提示:NULL");while(getchar()!=10);printf("\n");}//其他情况:数据来自客户端else{/***********************************数据读取部分***********************************///获取该客户端id号int active_id=client_id_ctl(client_arr,10,get_client_id,active_fd);struct PACK pack={0};int res=read(active_fd,&pack.size,4);if(res==0){printf("服务器提示:%d#用户断开连接\n",active_id);//[监视列表] 中 删除该描述夫epoll_ctl(epfd,EPOLL_CTL_DEL,active_fd,&event_serverfd);//移除该用户idclient_id_ctl(client_arr,10,remove_clinet,active_id);close(active_fd);continue;}read(active_fd,(char *)&pack+4,pack.size-4);/***********************************数据处理部分***********************************///根据PACK包中的 tyoe 决定对数据的操作if(pack.type==send_to_server){//说明 数据发向服务器 在服务器解析printf("服务器提示:%d#用户发来数据\n",active_id);unlood(&pack);	//解析数据}else if(pack.type>=0&&pack.type<10){//说明 数据需要转发给其他客户端printf("服务器提示:%d#用户发来数据 数据转发给%d#用户\n",active_id,pack.type);//获取 转发目标 的描述符int target_fd=client_id_ctl(client_arr,10,get_clinetfd,pack.type);//数据转发pack.type=active_id;write(target_fd,&pack,pack.size);		//数据发送给 转发目标}}}}return 0;
}int client_id_ctl(int arr[],int len,int cmd,int client)
{static int sum_client=0;//分配客户端id号if(cmd==insect_clinet){static int new_client_id=0;		//循环未使用的方式 给新增的客户端编号if(sum_client==10){printf("连接的客户端达到上限 \n");return -1;}//定位到 未使用的下标 未使用则数组的值为-1while (arr[new_client_id]%len!=-1){new_client_id=(new_client_id+1)%len;	//循环查找}arr[new_client_id]=client;	//将客户端描述符存入数组sum_client++;return new_client_id;}//获取客户端id号else if (cmd==get_client_id){for(int i=0;i<len;i++){if(arr[i]==client){return i;	//返回该客户端id号}}return -1;}//获取指定客户端描述符	根据id号查找else if (cmd==get_clinetfd){int id=client;if(id>=len||id<0){printf("id号不合法\n");return -1;	//删除失败 返回-1}return arr[id];	//成功返回该id号的描述符}//移除客户端id号	根据id号销毁else if (cmd==remove_clinet){int id=client;if(id>=len||id<0){printf("%d#客户端移除失败 id号不合法\n",id);return -1;	//删除失败 返回-1}arr[client]=-1;sum_client--;return id;		//删除成功 返回删除的id号}
}void unlood(struct PACK *pack)
{int len;while(1){//读取2个字节len=*(short *)(pack->buf+pack->conut);if(len==0){printf("数据解析完毕\n\n");return;}pack->conut+=2;//读取字符串char data[len+1];memset(data,0,sizeof(data));memcpy(data,pack->buf+pack->conut,len);printf("数据:%s\n",data);pack->conut+=len;}printf("数据解析完毕\n");
}

客户端

struct PACK
{int size;	//数据包大小int type;	//对数据的操作char buf[1500];	//数据int conut;	//数据已经占用多少字节
};//数据附加 放入数据
void append(struct PACK* pack,const char *data);
//解析数据
void unlood(struct PACK *pack);int main(int argc, const char *argv[])
{//创建套接字int clientfd=socket(AF_INET,SOCK_STREAM,0);	//ipv4 tcp形式 自动选择协议//ip地址 和 potr端口号 放入"网络通信结构体"struct sockaddr_in addr;addr.sin_family=AF_INET;	//ipv4int port=atoi(argv[1]);addr.sin_port=htons(port);	//端口号addr.sin_addr.s_addr=inet_addr("127.0.0.1");	//服务器ip地址//结构体数据 写入 套接字int res=connect(clientfd,(struct sockaddr*)&addr,sizeof(addr));if(res==-1){printf("套接字 绑定ip和port 失败\n");perror("bind");return 0;}int epfd=epoll_create1(EPOLL_CLOEXEC);  //创建动态的监视列表//设定 需要监视的描述符 以及 激活形式struct epoll_event event_serverfd={.events=EPOLLIN,.data.fd=clientfd};struct epoll_event event_stdinfd={.events=EPOLLIN,.data.fd=0};//添加到 [监视列表]epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&event_serverfd);epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event_stdinfd);while(1){printf("开始监视\n");struct epoll_event active_list[10];    //用于存放被激活的描述符//开始监视int len=epoll_wait(epfd,active_list,10,-1);    //被激活的描述符 在 active_list[10]数组中//遍历被激活的描述符for(int i=0;i<len;i++){int active_fd=active_list[i].data.fd;   //获取被激活的描述符fdif(active_fd==0){//如果是终端可读struct PACK pack={0};pack.type=-1;	//默认发往终端char buf[20]="";scanf("%19s",buf);printf("\n客户端数据:%s\n",buf);while(getchar()!=10);append(&pack,buf);printf("-----------------------------------\n");printf("对该数据的操作:\n");printf("  >= 0	转发到指定客户端\n");printf("  ==-1	数据发向服务器 服务器显示数据\n");printf("  ==-2	向服务器分配给自己的id号\n");printf("-----------------------------------\n");printf("该数据需要:");scanf("%d",&pack.type);while(getchar()!=10);pack.size=pack.conut+8;	//包实际大小write(clientfd,&pack,pack.size);		//发送给服务器 将接受多少字节pack.conut=0;	//发送完毕 conut 重置}if(active_fd==clientfd){//如果是描述符可读	说明服务器发来消息struct PACK pack={0};int res=read(clientfd,&pack.size,4);if(res==0){printf("与服务器断开连接\n");return 0;}read(clientfd,(char *)&pack+4,pack.size-4);printf("服务器传来一条数据,数据转发自%d#用户\n",pack.type);unlood(&pack);	//解析数据}}}return 0;
}//数据附加 放入数据
void append(struct PACK* pack,const char *data)
{int len=strlen(data);*(short *)(pack->buf+pack->conut)=len;pack->conut+=2;memcpy(pack->buf+pack->conut,data,len);pack->conut+=len;
}
//解析数据
void unlood(struct PACK *pack)
{int len;while(1){//读取2个字节len=*(short *)(pack->buf+pack->conut);if(len==0){printf("数据解析完毕\n\n");return;}pack->conut+=2;//读取字符串char data[len+1];memcpy(data,pack->buf+pack->conut,len);printf("数据:%s\n",data);pack->conut+=len;}printf("数据解析完毕\n");
}

 效果

           

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 同步锁性能的最佳实践:从理论到实践的完整指南

目录 一、同步锁性能分析 &#xff08;一&#xff09;性能验证说明 1. 使用同步锁的代码示例 2. 不使用同步锁的代码示例 3. 结果与讨论 &#xff08;二&#xff09;案例初步优化分析说明 1. 使用AtomicInteger原子类尝试优化分析 2. 对AtomicInteger原子类进一步优化 …

Mac之JDK安装

Mac之JDK安装 一.安装 jdk 打开终端输入命令:java -version 查看是否已安装 JDK Oracle 官方下载地址 根据自己Mac 系统安装 查看 Mac 系统&#xff0c;打开中断命令&#xff0c;输入: uname -a Compressed Archive 是压缩文档&#xff0c;下载的是一个 .tar.gz 压缩包 D…

[MySQL]5-MySQL扩展(分片)

随着数据量和用户量增加&#xff0c;MySQL会有读写负载限制。以下是部分解决方案 目录 功能拆分 使用读池拓展读&#xff08;较复杂&#xff09; 排队机制 &#x1f31f;分片拓展写 按业务或职责划分节点或集群 大数据集切分 分片键的选择 多个分片键 跨分片查询 资料…

芯盾时代数据安全产品体系,筑牢数据安全防线

芯盾时代数据安全治理&#xff08;DSG&#xff09;框架&#xff0c;以国家法律法规、行业监管标准、行业最佳实践为依据&#xff0c;从数据安全战略出发&#xff0c;以数据分类分级为支撑&#xff0c;构数据安全管理体系、数据安全技术体系、数据安全运营体系与数据安全监督评价…

腾讯大数据基于 StarRocks 的向量检索探索

作者&#xff1a;赵裕隆&#xff0c;腾讯大数据研发工程师 本文整理自腾讯大数据工程师在 StarRocks 年度峰会上的分享&#xff0c;深入探讨了向量检索技术的原理与应用。此功能已应用到腾讯内部多个场景&#xff0c;引入 StarRocks 后&#xff0c;业务不仅不需要维护多套数据库…

STM32 RTC 实时时钟说明

目录 背景 RTC(实时时钟)和后备寄存器 32.768HZ 如何产生1S定时 RTC配置程序 第一次上电RTC配置 第1步、启用备用寄存器外设时钟和PWR外设时钟 第2步、使能RTC和备份寄存器访问 第3步、备份寄存器初始化 第4步、开启LSE 第5步、等待LSE启动后稳定状态 第6步、配置LSE为…

android studio在gradle的build时kaptDebugKotlin这个task需要执行很久

只修改了一点java代码&#xff0c;kaptDebugKotlin这个任务却执行了3~5分钟。。。

机器学习(李宏毅)——self-Attention

一、前言 本文章作为学习2023年《李宏毅机器学习课程》的笔记&#xff0c;感谢台湾大学李宏毅教授的课程&#xff0c;respect&#xff01;&#xff01;&#xff01; 二、大纲 何为self-Attention&#xff1f;原理剖析self-Attention VS CNN、RNN、GNN 三、何为self-Attenti…

【Java使用Geotools对shp文件进行读取,读取完成之后shp、dbf、shx文件总是被占用,无法删除,如何解决呢?】

Java使用Geotools对shp文件进行读取&#xff0c;读取完成之后shp、dbf、shx文件总是被占用&#xff0c;无法删除&#xff0c;如何解决呢&#xff1f; 问题描述原因分析与问题解决1.直接原因2.解决方案 问题描述 Java使用Geotools对shp文件进行读取&#xff0c;读取完成之后.sh…

lvs的DR模式

基于Linux的负载均衡集群软件 LVS 全称为Linux Virtual Server,是一款开源的四层(传输层)负载均衡软件 Nginx 支持四层和七层(应用层)负载均衡 HAProxy 和Nginx一样,也可同时支持四层和七层(应用层)负载均衡 基于Linux的高可用集群软件 Keepalived Keepalived是Linux…

基于进化式大语言模型的下一代漏洞挖掘范式:智能对抗与自适应攻防体系

摘要 本文提出了一种基于进化式大语言模型(Evolutionary LLM)的智能漏洞挖掘框架,突破了传统静态分析的局限,构建了具备对抗性思维的动态攻防体系。通过引入深度强化学习与多模态感知机制,实现了漏洞挖掘过程的自适应进化,在RCE、SQLi、XXE等关键漏洞类型的检测中达到97…

java项目之基于SSM会议管理系统的设计与实现源码(ssm+mysql)

项目简介 基于SSM会议管理系统的设计与实现实现了以下功能&#xff1a; 基于SSM会议管理系统的设计与实现的主要使用者分为&#xff1a;管理员登录后修改个人的密码。用户管理中&#xff0c;对公司内的用户进行管理&#xff0c;包括会议管理员和员工&#xff0c;管理部门信息…

Linux第106步_Linux内核RTC驱动实验

1、了解rtc_device结构体 1)、打开“include/linux/rtc.h” rtc_class_ops是需要用户根据所使用的RTC设备编写的,其结构体如下: struct rtc_class_ops { int (*ioctl)(struct device *, unsigned int, unsigned long);/*函数指针ioctl*/ int (*read_time)(struct device *,…

java项目之基于推荐算法的图书购物网站源码(ssm+mybatis+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的基于推荐算法的图书购物网站项目。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于推荐算法的…

【Antv G2 5.x】饼图添加点击事件,获取当前坐标数据

// 监听 tooltip:show 事件this.chart.on(tooltip:show, (event) => {this.currentShowTooltipName = event.data.items[0].name})// 监听绘图区plot的点击事件this.chart.on(interval:click, ev => {this.$emit(chartClick, this.currentShowTooltipName);})// 监听绘图…

称呼计算器:智能科技,简化您的计算生活

一款手机应用程序&#xff0c;安卓设备上使用。这款计算器应用以其简洁的界面、实用的功能和良好的用户体验而受到用户的喜爱。 计算器的主要特点包括&#xff1a; 基本计算功能&#xff1a;支持加、减、乘、除等基本运算。 科学计算器模式&#xff1a;提供更高级的数学运算功…

STM32 裸机 C编程 vs micropython编程 vs linux python

以led点亮为例。 STM32 裸机 C编程需要设置时钟&#xff0c;管脚。 static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct {0};// GPIO端口时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA5为推挽输出模式GPIO_InitStruct.Pin GPIO_PIN_5;GPIO_InitStruct.M…

Spring boot(maven) - Mybatis 超级入门版

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

清华大学新闻与传播学院沈阳团队出品的《DeepSeek:从入门到精通》104页PDF

前言 本机运行DeepSeek R1大模型文章如下&#xff1a; Windows电脑本地部署运行DeepSeek R1大模型&#xff08;基于Ollama和Chatbox&#xff09;【保姆级万字教程】在Windows计算机部署DeepSeek大模型&#xff0c;给在实验室无外网的同事们用&#xff08;基于Ollama和OpenWebUI…

kbengine服务器和 数据库 系统路径配置

一、服务器 系统路径配置 二、mysql5.7.44 系统路径配置 mysql 压缩包安装方式 解压压缩包&#xff0c;将解压路径加入 系统环境。 或者 系统变量新增 变量名&#xff1a;MYSQL_HOME 变量值&#xff1a;C:\MyPrograms\mysql-8.0.12-winx64修改系统变量的 path 变量&#xff…