当前位置: 首页 > news >正文

网络编程 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");
}

 效果

           

相关文章:

网络编程 day3

思维导图 以select函数模型为例 思维导图2 对应 epoll模型 应使用的函数 题目 使用epoll函数实现 两个客户端 通过服务器 实现聊天 思路 在原先代码基础上 实现 服务器 发向 客户端 使用客户端在服务器上的 套接字描述符 实现 客户端 接收 服务器…...

Excel 融合 deepseek

效果展示 代码实现 Function QhBaiDuYunAIReq(question, _Optional Authorization "Bearer ", _Optional Qhurl "https://qianfan.baidubce.com/v2/chat/completions")Dim XMLHTTP As ObjectDim url As Stringurl Qhurl 这里替换为你实际的URLDim postD…...

【论文笔记】Are Self-Attentions Effective for Time Series Forecasting? (NeurIPS 2024)

官方代码https://github.com/dongbeank/CATS Abstract 时间序列预测在多领域极为关键&#xff0c;Transformer 虽推进了该领域发展&#xff0c;但有效性尚存争议&#xff0c;有研究表明简单线性模型有时表现更优。本文聚焦于自注意力机制在时间序列预测中的作用&#xff0c;提…...

游戏手柄Type-c方案,支持一边充电一边传输数据

乐得瑞推出LDR6023SS&#xff0c;专门针对USB-C接口手机手柄方案&#xff0c;支持手机快充&#xff0c;支持任天堂游戏机&#xff0c;PS4等设备~同时支持手机充电跟数据传输 1、概述 LDR6023SS SSOP16 是乐得瑞科技针对 USB Type-C 标准中的 Bridge 设备而开发的双 USB-C DRP …...

2. 4 模块化JDK:JDK模块结构与核心模块

第3章&#xff1a;模块化JDK&#xff1a;JDK模块结构与核心模块 JDK 9 将自身拆分为一系列模块&#xff0c;彻底告别传统的“单一JAR&#xff08;如 rt.jar&#xff09;”模式。本章深入解析 JDK 的模块化架构、核心模块功能及开发者如何高效利用这些模块。 3.1 JDK 模块化设计…...

每日一题——缺失的第一个正整数

缺失的第一个正整数 题目描述进阶&#xff1a;数据范围&#xff1a; 示例示例 1示例 2示例 3 题解思路代码实现代码解释复杂度分析总结 题目描述 给定一个无重复元素的整数数组 nums&#xff0c;请你找出其中没有出现的最小的正整数。 进阶&#xff1a; 时间复杂度&#xff…...

CEF132 编译指南 MacOS 篇 - 基础开发工具安装实战 (二)

1. 引言 在 macOS 平台上编译 CEF132 之前&#xff0c;首要任务是搭建一个完善的开发环境。与 Windows 和 Linux 环境不同&#xff0c;macOS 的开发环境主要以 Xcode 为核心。本篇将作为 CEF132 编译指南系列的第二篇&#xff0c;详细指导读者如何在 macOS 系统上安装和配置 X…...

vi 是 Unix 和 Linux 系统中常用的文本编辑器

vi是 Unix 和 Linux 系统中常用的文本编辑器&#xff0c;它有几种不同的模式&#xff0c;其中最常用的是命令模式和插入模式。光标控制主要在命令模式下进行&#xff0c;以下是一些常用的vi命令来控制光标位置&#xff1a; • h,j,k,l&#xff1a;分别用于将光标向左、向下、向…...

SwanLab x verl:可视化LLM强化学习后训练教程

文章目录 介绍Verl和SwanLab1. 环境安装2. 使用方法3. 查看训练日志 介绍Verl和SwanLab verl 是一个灵活、高效且可用于生产环境的强化学习&#xff08;RL&#xff09;训练框架&#xff0c;专为大型语言模型&#xff08;LLMs&#xff09;的后训练设计。它由字节跳动火山引擎团…...

职场到校园,初心未改:我的2024年

Hi&#xff0c;大家好&#xff0c;我是几何心凉。 其实早就想写一份复盘文章&#xff0c;正好借助2024年度博客之星的评选机会&#xff0c;来写下这篇总结。回望过去&#xff0c;感慨颇多。自从加入CSDN平台以来&#xff0c;已经见证了许多博主的来去匆匆&#xff0c;各类创作…...

C++基础知识学习记录—引用

1、引用的概念 概念&#xff1a;引用相当于给变量取个别名 对引用进行操作与直接操作变量相同&#xff0c;注意引用类型与变量类型一致 #include<iostream> using namespace std; int main(){int a10;int& cite_a a;//操作引用cite_a 与操作变量a完全一样cout &l…...

AWS Savings Plans 监控与分析工具使用指南

一、背景介绍 1.1 什么是 Savings Plans? AWS Savings Plans 是一种灵活的定价模式,通过承诺持续使用一定金额的 AWS 服务来获得折扣价格。它可以帮助用户降低 AWS 使用成本,适用于 EC2、Fargate 和 Lambda 等服务。 1.2 为什么需要监控? 优化成本支出跟踪使用情况评估投…...

【AI学习】关于 DeepSeek-R1的几个流程图

遇见关于DeepSeek-R1的几个流程图&#xff0c;清晰易懂形象直观&#xff0c;记录于此。 流程图一 来自文章《Understanding Reasoning LLMs》&#xff0c; 文章链接&#xff1a;https://magazine.sebastianraschka.com/p/understanding-reasoning-llms?continueFlagaf07b1a0…...

C++ ——从C到C++

1、C的学习方法 &#xff08;1&#xff09;C知识点概念内容比较多&#xff0c;需要反复复习 &#xff08;2&#xff09;偏理论&#xff0c;有的内容不理解&#xff0c;可以先背下来&#xff0c;后续可能会理解更深 &#xff08;3&#xff09;学好编程要多练习&#xff0c;简…...

【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案

建筑设计公司在项目执行过程中&#xff0c;会产生大量的设计图纸、效果图、实景照片等图片资料。这些资料按照项目名称、阶段、专业等维度存放在多个文件夹和子文件夹中。 操作需求&#xff1a;为了方便内部管理和向客户交付完整的设计方案&#xff0c;公司需要将每个项目文件…...

前端学习之Flex布局

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Flex布局示例</title><style>.conta…...

游戏引擎学习第97天

回顾昨天并计划今天 在这期节目中&#xff0c;主要讲解了光照的概念&#xff0c;并进一步讨论了法线贴图光照的实现。节目的内容大致分为几个部分&#xff1a; 光照的基础概述&#xff1a;讨论了光的工作原理以及如何在编程图形时需要考虑光照问题。尽管这些概念并没有深入到…...

Mysql中存储引擎各种介绍以及应用场景、优缺点

概述 MySQL 提供了多种存储引擎&#xff0c;每种引擎有不同的特点和适用场景。以下是几种常见的 MySQL 存储引擎的详细介绍&#xff0c;包括它们的底层工作原理、优缺点&#xff0c;以及为什么 MySQL 默认选择某种引擎。 1. InnoDB 底层工作原理&#xff1a; 事务支持&#…...

PHP 运算符

PHP 运算符 概述 PHP 是一种广泛使用的开源服务器端脚本语言,它具有丰富的运算符集,这些运算符是编写 PHP 程序的基础。运算符用于执行各种数学、逻辑和比较操作。本篇文章将详细介绍 PHP 中常用的运算符,包括算术运算符、比较运算符、逻辑运算符、赋值运算符等。 算术运…...

Vue全流程--Vue3.0与Vue2.0响应式原理对比

Vue2中数据的响应式 需要使用Vue.set这么一个api&#xff0c;修改数据 需要使用Vue.delete这么一个api&#xff0c;删除数据 数据代理这个当面的理解可以看看我前面文章Vue全流程--数据代理的理解以及在Vue中的应用-CSDN博客 Vue3中数据的响应式 Vue3使用proxy这个api实现…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...