02.10 TCP之文件传输
1.思维导图
2.作业
服务器代码:
#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里面的数据发给自己
}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;}}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");break;}else{if(user_arr[res].sock == 0){// 不在线}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;
}
相关文章:
02.10 TCP之文件传输
1.思维导图 2.作业 服务器代码: #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> …...
基于STM32的ADS1230驱动例程
自己在练手项目中用到了ADS1230,根据芯片手册自写的驱动代码,已测可用,希望对将要用到ADS1230芯片的人有所帮助。 芯片:STM32系列任意芯片、ADS1230 环境:使用STM32CubeMX配置引脚、KEIL 部分电路: 代码…...
Bro想要玩github api
Bro想要在vscode 和 rest client插件的帮助下,修改我的github个人信息 ### 先安装REST client插件 ### 文件名test-github.http ### bro需要自己在github develop setting 获得token ### ref link: https://docs.github.com/en/authentication/keeping-your-accoun…...
idea插件开发,如何获取idea设置的系统语言
手打不易,如果转摘,请注明出处! 注明原文:https://zhangxiaofan.blog.csdn.net/article/details/145578160 版本要求 大于 2024.3 错误用法 网上有的说使用:UIUtil com.intellij.util.ui.UIUtil 代码示例…...
怎麼使用靜態住宅IP進行多社媒帳號管理
隨著社交媒體平臺的多樣化,很多人發現一個社媒帳號已經無法滿足需求。以下是幾個常見場景: 企業需求:企業可能需要在不同平臺上運營多個品牌帳號,為每個市場地區單獨設立帳號。個人需求:一些自由職業者或內容創作者可…...
InfiniBand与IP over InfiniBand(IPOIB):实现高性能网络通信的底层机制
在现代高性能计算(HPC)和数据中心环境中,网络通信的效率和性能至关重要。InfiniBand(IB)作为一种高性能的串行计算机总线架构,以其低延迟、高带宽和高可靠性而广泛应用于集群计算和数据中心。IP over InfiniBand(IPOIB)则是在InfiniBand网络上实现IP协议的一种方式,它…...
掌握 PHP 单例模式:构建更高效的应用
在 PHP 应用开发中,资源的高效管理至关重要。单例模式是一种能够帮助我们实现这一目标的设计模式。本文将深入探讨单例模式的概念、工作原理以及在 PHP 项目中何时应该(或不应该)使用它。 什么是单例模式? 单例模式是一种设计模…...
实现限制同一个账号最多只能在3个客户端(有电脑、手机等)登录(附关键源码)
如上图,我的百度网盘已登录设备列表,有一个手机,2个windows客户端。手机设备有型号、最后登录时间、IP等。windows客户端信息有最后登录时间、操作系统类型、IP地址等。这些具体是如何实现的?下面分别给出android APP中采集手机信…...
Python入门全攻略(四)
函数 初识函数 函数:封装具有某种功能的代码块。 函数定义 使用def关键字来定义函数 # 定义函数 def 函数名(): 代码块 # 调用函数 函数名() 函数参数 def 函数名(形参) 代码块 # 调用 函数名(实参) 位置参数 按参数顺序传参 def func(a, b): print(a b)…...
Ubuntu 22.04 - OpenLDAP安装使用(服务器+LAM+客户端)
csdn你…怎么不自动保存了很崩溃啊啊啊啊,我记得发现没保存之后我又改了一遍然后保存了,怎么现在又没了啊啊啊啊,过了这么久我都不记得了啊啊啊啊啊 参考 在 Ubuntu 22.04|20.04|18.04 上安装 OpenLDAP 和 phpLDAPadmin 在 Ubuntu 22.04|20…...
Linux ARM64 将内核虚拟地址转化为物理地址
文章目录 前言一、通用方案1.1 kern_addr_valid1.2 __pa 二、ARM64架构2.1 AT S1E1R2.2 is_kernel_addr_vaild2.3 va2pa_helper 三、demo演示参考资料 前言 本文介绍一种通用的将内核虚拟地址转化为物理地址的方案以及一种适用于ARM64 将内核虚拟地址转化为物理地址的方案&…...
使用 Visual Studio Code (VS Code) 开发 Python 图形界面程序
安装Python、VS Code Documentation for Visual Studio Code Python Releases for Windows | Python.org 更新pip >python.exe -m pip install --upgrade pip Requirement already satisfied: pip in c:\users\xxx\appdata\local\programs\python\python312\lib\site-pa…...
图像处理篇---基本OpenMV图像处理
文章目录 前言1. 灰度化(Grayscale)2. 二值化(Thresholding)3. 掩膜(Mask)4. 腐蚀(Erosion)5. 膨胀(Dilation)6. 缩放(Scaling)7. 旋转…...
一文讲清springboot所有注解
Spring Boot 注释是提供有关 Spring 应用程序信息的元数据。 基于 Spring 构建,涵盖其所有功能, Spring Boot 因其生产就绪环境而迅速成为开发人员的最爱,它允许开发人员直接专注于逻辑,而无需配置和设置的麻烦。 Spring Boot 是一…...
pytest测试专题 - 1.1 运行pytest
<< 返回目录 1 pytest学习笔记 - 1.1 运行pytest 1.1 运行pyest 在命令行执行pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ... ...1.1.1 pytest不携带参数 pytest不带参数时,会扫描当前目录下的所有目录、子目录中符合测试用…...
Java多线程——线程池的使用
线程饥饿死锁 在单线程的Executor中,如果任务A将任务B提交给同一个Executor,并且等待任务B的结果,就会引发死锁线程池中所有正在执行任务的线程由于等待其他仍处于工作队列中的任务而阻塞 执行时间较长的任务 执行时间较长的任务不仅会造成…...
NO.15十六届蓝桥杯备战|while循环|六道练习(C++)
while循环 while语法形式 while 语句的语法结构和 if 语句⾮常相似,但不同的是 while 是⽤来实现循环的, if 是⽆法实现循环的。 下⾯是 while 循环的语法形式: //形式1 while ( 表达式 )语句; //形式2 //如果循环体想包含更多的语句&a…...
DeepSeek 从入门到精通学习指南,2025清华大学《DeepSeek从入门到精通》正式发布104页pdf版超全解析
DeepSeek 是一款强大的 AI 搜索引擎,广泛应用于企业级数据检索和分析。无论您是初学者还是有经验的用户,掌握 DeepSeek 的使用都能为您的工作带来极大的便利。本文将从入门到精通,详细介绍如何学习和使用 DeepSeek。 链接: https://pan.baid…...
2025年SEO自动优化工具
随着2025年互联网的快速发展,越来越多的企业和个人意识到,拥有一个排名靠前的网站对于吸引客户、增加流量、提高转化率至关重要。而要想让自己的网站脱颖而出,获得更多曝光,最重要的一项工作就是进行SEO优化。传统的SEO优化方式通…...
KEPServerEX 的接口类型与连接方式的详细说明
目录 一、KEPServerEX 核心架构 二、KEPServerEX 支持的接口类型 三、KEPServerEX 支持的连接类型 1. 通用工业协议 2. 品牌专属协议 3. 行业专用协议 4. 数据库与文件接口 四、配置示例 1. 接口配置(以OPC UA为例) 2. 连接配置(以…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...
鸿蒙Navigation路由导航-基本使用介绍
1. Navigation介绍 Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(Nav…...
