EX_25/2/10
epoll实现多路客户端之间的登录注册及消息和文件传输
服务器部分
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <sys/epoll.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;enum Type{TYPE_REGIST,TYPE_LOGIN,TYPE_CHAT,TYPE_FILE_UPLOAD_REQUEST
};typedef struct Pack{enum Type type;char name[20];char pswd[20];char filename[20];long filesize;char tarname[20];char text[1024];
}pack_t;// 李四:你好
typedef struct User{char name[20];char pswd[20];int sock;int hasMsg; // 用来表示当前客户端是否拥有未读消息的数据:0表示不拥有,1表示拥有未读消息char msg[1024]; // 用来缓存针对该用户的未读消息
// 当该用户上线的时候,检索一下hasMsg是0还是1,如果是1,就将msg里面的数据发给自己int hasfileMsg;char fileMsg[1024];}user_t;user_t user_arr[50] = {0};
int user_len = 0;void read_data(int client);
void insert_user(user_t user);
int find_user(const char* username);int main(int argc, const char *argv[])
{if(argc != 2){printf("请输入端口号\n");return 1;}// ./server 8888int port = atoi(argv[1]);// 将字符串 8888 转换成int类型port// 创建服务器套接字int server = socket(AF_INET,SOCK_STREAM,0);// 准备网络地址结构体:struct sockaddr_inaddr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("0.0.0.0");// 为套接字绑定ip 和 portif(bind(server,(addr_t*)&addr,sizeof(addr)) == -1){perror("bind");return 1;}// 监听listen(server,10);// 创建监视列表int epfd = epoll_create1(EPOLL_CLOEXEC);// 将 0 和 server 存入监视列表 epfd 中struct epoll_event epoll_stdin = {.events = EPOLLIN,.data.fd = 0};struct epoll_event epoll_server = {.events = EPOLLIN,.data.fd = server};epoll_ctl(epfd,EPOLL_CTL_ADD,0,&epoll_stdin);epoll_ctl(epfd,EPOLL_CTL_ADD,server,&epoll_server);// 准备一个数组,用来存放所有激活的描述符struct epoll_event arr[50] = {0};while(1){int len = epoll_wait(epfd,arr,50,-1);for(int i=0;i<len;i++){int fd = arr[i].data.fd;// 将到底是哪个描述符激活了单独取出来if(fd == server){printf("有新客户端连接\n");int client = accept(server,0,0);struct epoll_event epoll_client = {.events = EPOLLIN,.data.fd = client};epoll_ctl(epfd,EPOLL_CTL_ADD,client,&epoll_client);}else if(fd == 0){char buf[64] = "";scanf("%63s",buf);while(getchar()!=10);printf("键盘输入数据:%s\n",buf);}else{read_data(fd);}}}return 0;
}void insert_user(user_t user){user_arr[user_len] = user;user_len ++;
}int find_user(const char* username){for(int i=0;i<user_len;i++){if(strcmp(username,user_arr[i].name) == 0){return i;}}return -1;
}void read_data(int client){//while(1){pack_t pack = {0};int res = read(client,&pack,sizeof(pack));//if(res == 0){break;}switch(pack.type){case TYPE_REGIST:{int res = find_user(pack.name);// 根据用户发来的账号,在数组中查询是否存在char* msg = NULL;if(res == -1){ // 如果不存在返回-1user_t user = {0};strcpy(user.name,pack.name);strcpy(user.pswd,pack.pswd);insert_user(user);msg = "注册成功";}else{// 如果存在,返回这个账号在数组中的下标位置msg = "该账号已存在";}strcpy(pack.text,msg);write(client,&pack,sizeof(pack));break;}case TYPE_LOGIN:{int res = find_user(pack.name);char* msg = NULL;if(res == -1){msg = "该账号不存在";}else{user_t user = user_arr[res];// 将找到的用户单独拎出来if(strcmp(user.pswd,pack.pswd) == 0){msg = "登录成功";user_arr[res].sock = client;// user_arr[res] 是根据当前登录用户发送过来的账号,找到的存放在数组中的用户结构体// client 是当前正在登录的用户在服务器的套接字if(user_arr[res].hasMsg == 1){// 说明当前用户存在未读消息pack_t pack = {0};pack.type = TYPE_CHAT;strcpy(pack.text , user_arr[res].msg); // 将缓存的消息写入 pack.text 里面write(client,&pack,sizeof(pack)); // 将缓存消息发给新登录的客户端user_arr[res].hasMsg = 0;}//存在未读文件if(user_arr[res].hasfileMsg==1){pack_t pack={0};pack.type=TYPE_FILE_UPLOAD_REQUEST;strcpy(pack.text,user_arr[res].fileMsg);write(client,&pack,sizeof(pack));user_arr[res].hasfileMsg=0;}}else{ msg = "密码错误";}}strcpy(pack.text,msg);write(client,&pack,sizeof(pack));break;}case TYPE_CHAT:{char* msg = NULL;// 聊天的时候,客户端会发来如下格式的信息 "张三:你好",表明 你好这条消息是发给张三的// 所以我们要根据张三的账号,找到张三的套接字,张三的姓名和套接字都存放在 结构体数组 user_arr里面int res = find_user(pack.tarname);if(res == -1){msg = "该用户不存在";strcpy(pack.text,msg);write(client,&pack,sizeof(pack));}else{user_t user = user_arr[res];// user_arr[res] 为准备接受聊天信息的用户if(user.sock == 0){//msg = "用户未登录";//strcpy(pack.text,msg);//write(client,&pack,sizeof(pack));user_arr[res].hasMsg = 1;// 表明当前用户存在了未读消息strcpy(user_arr[res].msg , pack.text); // 将准备发送给该用户的消息,缓存到user_arr[res].msg里面去}else{// 用户存在并登录的状态int tarsock = user.sock;write(tarsock,&pack,sizeof(pack));}}break;}case TYPE_FILE_UPLOAD_REQUEST:{//printf("接收到客户端文件上传请求:%s %s\n",pack.tarname,pack.filename);int res = find_user(pack.tarname);if(res == -1){// 没注册 今晚作业printf("该用户不存在\n");}else{if(user_arr[res].sock == 0){// 不在线,也是今晚作业user_arr[res].hasfileMsg=1;strcpy(user_arr[res].fileMsg,pack.text);}else{// 目标用户在线,直接将接收到的pack包转发给目标用户printf("转发文件中...\n");write(user_arr[res].sock,&pack,sizeof(pack));}}break;}}//}
}
客户端部分
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;enum Type{TYPE_REGIST,TYPE_LOGIN,TYPE_CHAT,TYPE_FILE_UPLOAD_REQUEST
};typedef struct Pack{enum Type type;char name[20];char pswd[20];char filename[20];long filesize;char tarname[20];char text[1024];
}pack_t;void* thread_main(void* arg){int client = *(int*)arg;while(1){pack_t pack = {0};int res = read(client,&pack,sizeof(pack));if(res == 0){break;}switch(pack.type){case TYPE_REGIST:{printf("%s\n",pack.text);break;}case TYPE_LOGIN:{printf("%s\n",pack.text);break;}case TYPE_CHAT:{printf("接收到消息:%s\n",pack.text);break;}case TYPE_FILE_UPLOAD_REQUEST:{char filename[128] = "./client_file_system/";// 获取文件名,方便打开文件strcat(filename , pack.filename);int fd = open(filename,O_CREAT | O_TRUNC | O_WRONLY,0666);// 获取文件长度long filesize = pack.filesize;long readed_size = 0;//printf("接收到文件名和文件大小\n");while(1){pack_t filepack = {0};int res = read(client,&filepack,sizeof(filepack)); // 读取别的客户端发来的文件内容if(res != sizeof(filepack)){printf("发生分包\n");}int size = strlen(filepack.text);//printf("接受到文件信息\n");write(fd,filepack.text,size);// 将接受到的文件内容,写入文件中去readed_size += size;if(readed_size >= filesize){close(fd);break;}}break;}}}
}int main(int argc, const char *argv[])
{if(argc != 2){printf("请输入端口号\n");return 1;}// ./server 8888int port = atoi(argv[1]);// 将字符串 8888 转换成int类型portint client = socket(AF_INET,SOCK_STREAM,0);addr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("192.168.60.77");if(connect(client,(addr_t*)&addr,sizeof(addr)) == -1){perror("connect");return 1;}pthread_t id;pthread_create(&id,0,thread_main,&client);pthread_detach(id);while(1){int ch = -1;printf("1:注册\n");printf("2:登录\n");printf("3:聊天\n");printf("4:发送文件\n");printf("0:退出\n");printf("请选择:");scanf("%d",&ch);while(getchar()!=10);switch(ch){case 1:{pack_t pack = {0};printf("请输入账号:");scanf("%s",pack.name);while(getchar()!=10);printf("请输入密码:");scanf("%s",pack.pswd);while(getchar()!=10);pack.type = TYPE_REGIST;write(client,&pack,sizeof(pack));break;}case 2:{pack_t pack = {0};printf("请输入账号:");scanf("%s",pack.name);while(getchar()!=10);printf("请输入密码:");scanf("%s",pack.pswd);while(getchar()!=10);pack.type = TYPE_LOGIN;write(client,&pack,sizeof(pack));break;}case 3:{// 聊天对象的姓名:聊天内容// 聊天对象的姓名放在pack.tarname里面// 聊天内容放在 pack.text 里面// 张三:你好pack_t pack = {0};pack.type = TYPE_CHAT;scanf("%s %s",pack.tarname,pack.text);printf("tarname = %19s\n",pack.tarname);while(getchar()!=10);write(client,&pack,sizeof(pack));break;}case 4:{pack_t pack = {0};pack.type = TYPE_FILE_UPLOAD_REQUEST;printf("请输入接受文件的用户名:");char tarname[20] = "";scanf("%19s",pack.tarname);strcpy(tarname,pack.tarname);while(getchar()!=10);printf("请输入想要发送的文件名:");scanf("%19s",pack.filename);while(getchar()!=10);int fd = open(pack.filename,O_RDONLY);if(fd == -1){printf("该文件不存在\n");break;}struct stat buf = {0};stat(pack.filename,&buf);pack.filesize = buf.st_size;write(client,&pack,sizeof(pack));while(1){pack_t pack = {0};pack.type = TYPE_FILE_UPLOAD_REQUEST;strcpy(pack.tarname,tarname);int res = read(fd,pack.text,1023);if(res == 0){break;}write(client,&pack,sizeof(pack));}close(fd);break;}case 0:{break;}}}return 0;
}
相关文章:
EX_25/2/10
epoll实现多路客户端之间的登录注册及消息和文件传输 服务器部分 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include…...
python视频爬虫
文章目录 爬虫的基本步骤一些工具模拟浏览器并监听文件视频爬取易错点一个代码示例参考 爬虫的基本步骤 1.抓包分析,利用浏览器的开发者工具 2.发送请求 3.获取数据 4.解析数据 5.保存数据 一些工具 requests, 用于发送请求,可以通过get,p…...

RbFT:针对RAG中检索缺陷的鲁棒性微调
今天给大家分享一篇最新的RAG论文: 论文题目:Enhancing Retrieval-Augmented Generation: A Study of Best Practices 论文链接:https://arxiv.org/pdf/2501.18365 论文代码:https://github.com/StibiumT16/Robust-Fine-tuning 研…...
证明: 极限的局部有界性
在考研数学中,极限的局部有界性是一个非常重要的概念,尤其是在讨论函数的连续性、可积性和可微性等性质时。局部有界性可以帮助我们理解函数在某些区域内的行为。 定理: 如果 lim x → x 0 f ( x ) L \lim_{x \to x_0} f(x) L limx→x0…...
51单片机俄罗斯方块计分函数
/************************************************************************************************************** * 名称:scoring * 功能:计分 * 参数:NULL * 返回:NULL * 备注:采用非阻塞延时 ****************…...
new 以及 call、apply、bind 关键字解析
1.new关键字 自动创建对象:使用new调用构造函数时,会自动创建一个空对象,并将其赋值给this。你不需要显式地使用{}来创建对象。 绑定this到新对象:构造函数内部的this指向新创建的对象,因此可以在构造函数中为新对象添…...
【用Deepseek搭建免费的个人知识库--综合教程(完整版)】第二篇:Ollama服务器
用Deepseek搭建免费的个人知识库–综合教程(完整版):第二篇:Ollama服务器部署 OLLAMA服务器的配置在很多网上都已经介绍的非常清楚了,我们的重点不在于那些简单的步骤,而是在需要为下一步做准备的地方更加…...

【图片合并转换PDF】如何将每个文件夹下的图片转化成PDF并合并成一个文件?下面基于C++的方式教你实现
医院在为患者进行诊断和治疗过程中,会产生大量的医学影像图片,如 X 光片、CT 扫描图、MRI 图像等。这些图片通常会按照检查时间或者检查项目存放在不同的文件夹中。为了方便医生查阅和患者病历的长期保存,需要将每个患者文件夹下的图片合并成…...

从基础到人脸识别与目标检测
前言 从本文开始,我们将开始学习ROS机器视觉处理,刚开始先学习一部分外围的知识,为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本,系统采用Ubuntu20.04,ROS采用noetic。 颜…...

Elasticsearch:在 Elastic 中玩转 DeepSeek R1 来实现 RAG 应用
在这个春节,如一声春雷,DeepSeek R1 横空出世。现在人人都在谈论 DeepSeek R1。这个大语言模型无疑在中国及世界的人工智能发展史上留下了重要的里程碑。那么我们改如何结合 DeepSeek R1 及 Elasticsearch 来实现 RAG 呢?在之前的文章 “使用…...

寒假2.6--SQL注入之布尔盲注
知识点 原理:通过发送不同的SQL查询来观察应用程序的响应,进而判断查询的真假,并逐步推断出有用的信息 适用情况:一个界面存在注入,但是没有显示位,没有SQL语句执行错误信息,通常用于在无法直接…...

CTF中特别小的EXE是怎么生成的
我们在打CTF时候,出题的爷爷们给出的exe都很小 就10k左右,有的甚至就5k,那时候我很郁闷啊。现在我也能了啊哈哈 不多bb按如下操作: 我们来看看正常的release生成的代码# Copy #include "windows.h" int main(){ Messa…...

git rebase 和 git merge的区别
Rebase 可使提交树变得很干净, 所有的提交都在一条线上。 Merge 则是包含所有的调试记录,合并之后,父级的所有信息都会合并在一起 Rebase 修改了提交树的历史 比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的…...

Gitlab中如何进行仓库迁移
需求:之前有一个自己维护的新仓库A,现在需要将这个仓库提交并覆盖另一个旧的仓库B,需要保留A中所有的commit信息。 1.方法一:将原有仓库A导出后再导入到新的仓库B中 适用场景:新的仓库B是一个待建仓库,相当…...

LabVIEW 开发航天项目软件
在航天项目软件开发中,LabVIEW 凭借其图形化编程优势被广泛应用。然而,航天项目的高可靠性、高精度及复杂环境适应性要求,使得在使用 LabVIEW 开发时,有诸多关键要点需要特别关注。本文将详细分析在开发航天项目软件时需要重点注意…...

深度整理总结MySQL——MySQL加锁工作原理
MySQL加锁工作原理 前言前置知识- 锁为什么加在索引上锁的粒度优化提高并发性避免全表扫描优化死锁处理解决幻读问题 什么SQL语句会加行级锁MySQL是如何加行级锁场景模拟代码唯一索引等值查询退化为记录锁为什么会退化为记录锁分析加了什么锁为什么会退化为间隙锁为什么我可以插…...

kafka专栏解读
kafka专栏文章的编写将根据kafka架构进行编写,即先编辑kafka生产者相关的内容,再编写kafka服务端的内容(这部分是核心,内容较多,包含kafka分区管理、日志存储、延时操作、控制器、可靠性等),最后…...
1-portal认证功能
很多时候公共网络需要提供安全认证功能,比如我们去星巴克或者商场、酒店,我们连接wifi上网的时候, 需要认证后才可以上网。 用户可以主动访问已知的Portal认证网站,输入用户名和密码进行认证,这种开始Portal认证的方式…...
MySQL面试题合集
1.MySQL中的数据排序是怎么实现的? 回答重点 排序过程中,如果排序字段命中索引,则利用 索引排序。 反之,使用文件排序。 文件排序中,如果数据量少则在内存中排序, 具体是使用单路排序或者双路排序。 如果数据大则利用磁盘文件进行外部排序,一 般使用归并排序。 知识…...

spring学习(druid、c3p0的数据源对象管理)(案例学习)
目录 一、博客引言。 二、阿里云-druid案例准备(依赖坐标、配置文件、测试类)。 (1)初始依赖坐标、配置文件与测试类。 (2)导入阿里云-druid依赖坐标。 (3)DruidDataSource。 (4)set…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...