day34 UDP套接字编程 可靠文件传输与实时双向聊天系统

UDP文件传输

实现客户端向服务器传输文件(如图片)的功能,确保传输后文件内容完全一致且可正常打开。传输过程采用简单的确认机制防止数据包丢失,传输完成后双方程序自动退出。核心知识点包括:UDP套接字创建、文件读写、数据包分块传输、传输结束标志处理。

客户端代码 (cli.c)

#include <arpa/inet.h>   // 提供IP地址转换函数
#include <fcntl.h>       // 提供文件控制操作
#include <netinet/in.h>  // 定义IPv4地址结构
#include <netinet/ip.h>  // 定义IP协议相关结构
#include <stdio.h>       // 标准输入输出函数
#include <stdlib.h>      // 标准库函数
#include <string.h>      // 字符串操作函数
#include <sys/socket.h>  // 套接字API
#include <sys/types.h>   // 数据类型定义
#include <time.h>        // 时间相关函数
#include <unistd.h>      // POSIX系统调用typedef struct sockaddr *(SA);  // 定义sockaddr指针别名,简化类型转换int main(int argc, char **argv)
{// 创建UDP套接字(AF_INET: IPv4, SOCK_DGRAM: 数据报套接字)int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");  // 套接字创建失败时打印错误return 1;}// 配置服务器地址结构struct sockaddr_in ser;ser.sin_family = AF_INET;         // 地址族:IPv4ser.sin_port = htons(50000);      // 端口号转网络字节序(50000)ser.sin_addr.s_addr = inet_addr("192.168.1.48");  // 服务器IP地址// 打开本地文件(只读模式)int fd = open("/home/linux/1.png", O_RDONLY);if (-1 == fd){perror("open error\n");return 1;}int num = 0;        // 累计已发送字节数char buf[1024] = {0};  // 数据缓冲区(1024字节块)while (1){bzero(buf, sizeof(buf));  // 清空缓冲区int ret = read(fd, buf, sizeof(buf));  // 从文件读取数据到缓冲区num += ret;  // 累计传输字节数printf("num:%d\n", num);  // 实时打印已发送字节数// 读取结束或出错时退出循环if (ret <= 0){break;}// 向服务器发送数据包sendto(sockfd, buf, ret, 0, (SA)&ser, sizeof(ser));// 接收服务器确认包(小阻塞:确保服务器处理完当前包)bzero(buf, sizeof(buf));recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);}// 发送传输结束标志strcpy(buf, "^_^");  // 结束标志字符串sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));// 关闭资源close(sockfd);close(fd);return 0;
}

服务器代码 (ser.c)

#include <arpa/inet.h>   // 提供IP地址转换函数
#include <netinet/in.h>  // 定义IPv4地址结构
#include <netinet/ip.h>  // 定义IP协议相关结构
#include <stdio.h>       // 标准输入输出函数
#include <stdlib.h>      // 标准库函数
#include <string.h>      // 字符串操作函数
#include <sys/socket.h>  // 套接字API
#include <sys/types.h>   // 数据类型定义
#include <unistd.h>      // POSIX系统调用
#include <time.h>        // 时间相关函数
#include <fcntl.h>       // 提供文件控制操作typedef struct sockaddr * (SA);  // 定义sockaddr指针别名int main(int argc, char **argv)
{// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");return 1;}// 配置服务器地址结构(用于绑定)struct sockaddr_in ser, cli;ser.sin_family = AF_INET;         // 地址族:IPv4ser.sin_port = htons(50000);      // 端口号转网络字节序ser.sin_addr.s_addr = inet_addr("192.168.1.48");  // 本机IP地址// 绑定套接字到指定地址和端口int ret = bind(sockfd, (SA) &ser, sizeof(ser));if (-1 == ret){perror("bind");return 1;}time_t tm;  // 时间变量(未实际使用,保留原代码)socklen_t len = sizeof(cli);  // 客户端地址结构长度// 创建新文件(写入/创建/截断模式,权限0666)int fd = open("2.png", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (-1 == fd){perror("open error\n");return 1;}int num = 0;  // 累计接收字节数while (1){char buf[1024] = {0};  // 数据缓冲区// 接收客户端数据包int ret = recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);num += ret;  // 累计接收字节数printf("num:%d\n", num);  // 实时打印已接收字节数// 检测到结束标志时退出循环if (0 == strcmp(buf, "^_^")){break;}// 将数据写入文件write(fd, buf, ret);// 发送确认包(解除客户端阻塞)bzero(buf, sizeof(buf));strcpy(buf, "go on");  // 确认消息sendto(sockfd, buf, strlen(buf), 0, (SA)&cli, len);}// 关闭资源close(sockfd);close(fd);return 0;
}

理想运行结果

  1. 文件传输过程
    • 客户端输出:num:1024num:2048 → … → num:[文件总大小](字节数递增)
    • 服务器输出:num:1024num:2048 → … → num:[文件总大小](与客户端一致)
  2. 传输验证
    • 传输完成后,2.png 与原始 1.png 大小完全相同(ls -l 对比)
    • 图片文件可正常打开(如 eog 2.png 无损坏)
  3. 退出机制
    • 客户端发送 ^_^ 后自动退出
    • 服务器收到 ^_^ 后自动退出
  4. 关键特性
    • 每个数据包传输后通过 recvfrom/sendto 实现简单确认,避免发送过快导致丢包
    • 结束标志 ^_^ 确保双方同步退出

UDP聊天

实现双向实时聊天功能,支持连续消息收发。输入 #quit 时,客户端和服务器同时退出。核心知识点包括:多进程并发处理(fork)、消息循环控制、退出信号同步。

服务器代码 (ser.c)

#include <arpa/inet.h>   // IP地址转换
#include <fcntl.h>       // 文件控制
#include <netinet/in.h>  // IPv4地址结构
#include <netinet/ip.h>  // IP协议结构
#include <signal.h>      // 信号处理(用于进程终止)
#include <stdio.h>       // 标准I/O
#include <stdlib.h>      // 标准库
#include <string.h>      // 字符串操作
#include <sys/socket.h>  // 套接字API
#include <sys/types.h>   // 数据类型
#include <time.h>        // 时间函数
#include <unistd.h>      // POSIX系统调用typedef struct sockaddr *(SA);  // sockaddr指针别名int main(int argc, char **argv)
{// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");return 1;}// 配置服务器地址结构struct sockaddr_in ser, cli;ser.sin_family = AF_INET;ser.sin_port = htons(50000);      // 监听端口50000ser.sin_addr.s_addr = inet_addr("192.168.1.48");  // 本机IP// 绑定套接字int ret = bind(sockfd, (SA)&ser, sizeof(ser));if (-1 == ret){perror("bind");return 1;}socklen_t len = sizeof(cli);  // 客户端地址长度char buf[512] = {0};          // 消息缓冲区// 等待客户端初始连接("start"消息)recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);// 创建子进程处理并发pid_t pid = fork();if (pid > 0)  // 父进程:接收客户端消息{while (1){bzero(buf, sizeof(buf));  // 清空缓冲区// 接收客户端消息recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);// 检测退出命令if (0 == strcmp(buf, "#quit\n")){kill(pid, 9);  // 终止子进程exit(1);       // 父进程退出}printf("cli:%s", buf);  // 显示客户端消息}}else if (0 == pid)  // 子进程:发送服务器消息{while (1){printf("to cli:");  // 提示输入char buf[512] = {0};fgets(buf, sizeof(buf), stdin);  // 读取用户输入// 发送消息到客户端sendto(sockfd, buf, strlen(buf), 0, (SA)&cli, len);// 检测退出命令if (0 == strcmp(buf, "#quit\n")){kill(getppid(), 9);  // 终止父进程exit(1);             // 子进程退出}}}else  // fork失败{perror("fork");return 1;}close(sockfd);  // 关闭套接字(实际不会执行到此处)return 0;
}

客户端代码 (cli.c)

#include <arpa/inet.h>   // IP地址转换
#include <netinet/in.h>  // IPv4地址结构
#include <netinet/ip.h>  // IP协议结构
#include <stdio.h>       // 标准I/O
#include <stdlib.h>      // 标准库
#include <string.h>      // 字符串操作
#include <sys/socket.h>  // 套接字API
#include <sys/types.h>   // 数据类型
#include <unistd.h>      // POSIX系统调用
#include <time.h>        // 时间函数
#include <fcntl.h>       // 文件控制
#include <signal.h>      // 信号处理typedef struct sockaddr * (SA);  // sockaddr指针别名int main(int argc, char **argv)
{// 创建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("socket");return 1;}// 配置服务器地址结构struct sockaddr_in ser;ser.sin_family = AF_INET;ser.sin_port = htons(50000);      // 目标端口50000ser.sin_addr.s_addr = inet_addr("192.168.1.48");  // 服务器IPsocklen_t len = sizeof(ser);  // 地址长度char buf[512] = {0};strcpy(buf, "start");  // 初始连接消息sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, len);  // 通知服务器// 创建子进程处理并发pid_t pid = fork();if (pid > 0)  // 父进程:接收服务器消息{while (1){bzero(buf, sizeof(buf));// 接收服务器消息recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);// 检测退出命令if (0 == strcmp(buf, "#quit\n")){kill(pid, 9);  // 终止子进程exit(1);       // 父进程退出}printf("ser:%s", buf);  // 显示服务器消息}}else if (0 == pid)  // 子进程:发送客户端消息{while (1){printf("to ser:");  // 提示输入char buf[512] = {0};fgets(buf, sizeof(buf), stdin);  // 读取用户输入// 发送消息到服务器sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, len);// 检测退出命令if (0 == strcmp(buf, "#quit\n")){kill(getppid(), 9);  // 终止父进程exit(1);             // 子进程退出}}}else  // fork失败{perror("fork");return 1;}close(sockfd);  // 关闭套接字(实际不会执行到此处)return 0;
}

理想运行结果

  1. 初始化连接

    • 客户端发送 start 消息,服务器开始监听
    • 双方进入消息循环
  2. 聊天过程

    • 服务器终端:
      to cli:Hello  // 服务器输入
      cli:How are you?  // 显示客户端消息
      to cli:#quit  // 输入退出命令
      
    • 客户端终端:
      ser:Hello  // 显示服务器消息
      to ser:How are you?  // 客户端输入
      ser:#quit  // 收到退出命令
      
  3. 退出机制

    • 任意一方输入 #quit 后:
      • 发送方立即终止对方进程(kill
      • 本地进程退出(exit(1)
    • 双方终端同时关闭,无残留进程
  4. 关键特性

    • 通过 fork 实现双工通信:父进程处理接收,子进程处理发送
    • 退出命令 #quit 触发双向终止(避免单方退出导致僵局)
    • 消息实时显示(printf 前无缓冲,即时刷新)

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

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

相关文章

策略模式-不同的鸭子的案例

介绍了策略模式在C#中的应用&#xff0c;以一个鸭子的例子来说明。首先定义了鸭子类以及鸭子的行为&#xff08;方法&#xff09;&#xff0c;然后通过继承和实现接口的方式来定义不同种类的鸭子的特性。介绍了策略模式的概念&#xff0c;将相同的算法封装在不同的类中&#xf…

C++语言编程规范-初始化和类型转换

01 C语言编程规范-常量 02 初始化和类型转换 声明、定义与初始化 03 禁止用 memcpy、memset 初始化非 POD 对象 说明&#xff1a;POD 全称是“Plain Old Data”&#xff0c;是 C 98 标准(ISO/IEC 14882, first edition, 1998-09-01)中引入的一个概念&#xff0c; PO…

从零构建一款开源在线客服系统:我的Go语言实战之旅

了解更多&#xff0c;搜索 "程序员老狼"用代码连接世界&#xff0c;让沟通无界限缘起&#xff1a;为什么选择开发客服系统&#xff1f;在数字化浪潮席卷全球的今天&#xff0c;企业与客户之间的沟通方式正在发生深刻变革。传统的电话和邮件支持已无法满足即时互动的需…

unsloth笔记:基本介绍

更快的速度、更省的内存训练、运行、评估大模型 1 支持的模型 All Our Models | Unsloth Documentation 1.1 Dynamic GGUF/instruct 4-bit llama.cpp使用的新模型格式&#xff0c;专为高效、本地推理设计注&#xff1a;GGUF无法微调 只保留推理所需的内容&#xff0c;如量化…

博众测控 | 一文看懂菊水电源产品在半导体测试中的应用

01 半导体在各行业上的应用半导体作为现代工业体系的“核心神经”&#xff0c;其性能参数与应用场景深度绑定&#xff0c;不同行业因核心设备的功能需求差异&#xff0c;对半导体的电流、电压承载能力及类型选择有着明确且严格的要求&#xff0c;具体应用细节如下&#xff1a;1…

【STM32】贪吃蛇 [阶段 8] 嵌入式游戏引擎通用框架设计

这篇博客是 承接&#xff1a;【项目思维】贪吃蛇&#xff08;嵌入式进阶方向&#xff09;中 嵌入式游戏引擎雏形&#xff08;终极进阶&#xff09;&#xff0c; 是我们此前从 “写一个小游戏”提升到“构建可复用游戏框架” 的飞跃阶段。我们以“贪吃蛇游戏”为例&#xff0c;抽…

Vue图标按钮好用的样式

图标按钮示例一 <template><div class"icon-button-group"><button class"icon-btn icon-btn--default"><i class"el-icon-moon"></i></button><button class"icon-btn icon-btn--primary"&g…

Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装

文章目录前言一、Web 概念1.1 Web 的基本概念1.1.1 Web的特点1.2 B/S 架构模型1.3 Web 请求与响应过程&#xff08;重点&#xff09;1.4 静态资源与动态资源1.5 Web 的发展阶段1.6 案例&#xff1a;搭建最小 Web 服务1.6.1 目标1.6.2 搭建步骤1.7 小结二、HTTP 与 HTTPS 协议2.…

一种用geoserver发布复杂样式矢量服务的方法

最近因为系统需要在国产系统中部署&#xff0c;遇见了国产系统不支持ArcGIS的尴尬局面&#xff0c;好在geoserver还是可以支持的&#xff0c;遂用geoserver解决服务问题。 在发布过程中&#xff0c;遇到比较难受的点就是矢量数据的样式配图&#xff0c;在我用QGIS配好导出sld后…

为什么神经网络网络算法比机器学习模型算法更加强大?

神经网络&#xff08;尤其是深度神经网络&#xff09;相比传统机器学习模型&#xff08;如线性回归、决策树、支持向量机等&#xff09;的“强大”主要体现在其更强的表达能力、自适应特征学习能力以及对复杂模式的建模能力。但这种“强大”并非绝对&#xff0c;而是有特定条件…

中国移动浪潮云电脑CD1000-系统全分区备份包-可瑞芯微工具刷机-可救砖

中国移动浪潮云电脑CD1000-系统全分区备份包-可瑞芯微工具刷机-可救砖 开启ADB教程&#xff1a; 可查看&#xff1a;浪潮CD1000-移动云电脑-RK3528芯片-232G-安卓9-开启ADB ROOT破解教程 可轻松打开了wifi adb和USB调试。 往期详细内容-文章&#xff1a;浪潮CD1000-移动云电脑…

C++两个字符串的结合

这段代码实现字符串拼接功能。用户输入两个字符串a和b后&#xff0c;使用append()方法将b追加到a后面&#xff0c;然后输出拼接后的结果。代码简洁但存在改进空间&#xff1a;1. 缺少输入验证 2. 直接修改原字符串a可能不符合某些场景需求 3. 可考虑更高效的拼接方式。适合基础…

UE4 Rider调试时添加自定义命令行参数

1、打开 Rider 右上角&#xff0c;针对你的项目&#xff08;例如叫做“Mini”&#xff09;打开 Edit 2、输入自定义的参数&#xff0c;如下图的例子是输入 -dx12 -norhithread &#xff0c;然后Apply并OK。3、开始调试&#xff08;虫子按钮&#xff09;

混合架构大型语言模型(Jamba)

Jamba是由AI21 Labs开发的混合架构大型语言模型&#xff08;LLM&#xff09;&#xff0c;结合了Transformer的语义理解能力和Mamba结构化状态空间模型&#xff08;SSM&#xff09;的高效性&#xff0c;旨在解决长文本处理中的计算瓶颈。 一、技术特点 1.混合架构设计 Jamba采用…

2025 年高教社杯全国大学生数学建模竞赛C 题 NIPT 的时点选择与胎儿的异常判定详解(一)

基于胎儿Y染色体浓度的孕周与BMI建模分析摘要本文利用某竞赛提供的胎儿Y染色体浓度数据&#xff0c;建立了以孕周和孕妇BMI为自变量的多项式回归模型&#xff0c;探讨了其对Y染色体浓度的影响。通过数据清洗与筛选&#xff0c;共获得1082条有效男胎样本。结果显示&#xff1a;Y…

PyTorch DDP 随机卡死复盘:最后一个 batch 挂起,NCCL 等待不返回

PyTorch DDP 随机卡死复盘&#xff1a;最后一个 batch 挂起&#xff0c;NCCL 等待不返回&#xff0c;三步修复 Sampler & drop_last很多人在接触深度学习的过程往往都是从自己的笔记本开始的&#xff0c;但是从接触工作后&#xff0c;更多的是通过分布式的训练来模型。由于…

计算机专业考研备考建议

对于全国硕士研究生招生考试&#xff08;考研&#xff09;&#xff0c;考试科目主要由两大部分组成&#xff1a;全国统一命题的公共课 和 由招生单位自主命题的专业课。具体的考试科目取决于你报考的专业和学校。下面我为你详细拆解&#xff1a;一、考试科目构成&#xff08;绝…

关于嵌入式学习——单片机1

基础整体概念以应用为中心:消费电子(手机、蓝牙耳机、智能音响)、医疗电子(心率脉搏、呼吸机)、无人机(大疆D)、机器人(人形四足机器人) 计算机技术:计算机五大组成:运算器(数据运算)、控制器(指令控制)、存储器(内存外存)、输入设备(鼠标、键盘、摄像头)、输出设备(显示器)软件…

LightDock.server liunx 双跑比较

LightDock: a new multi-scale approach to protein–protein docking The LightDock server is free and open to all users and there is no login requirement server 1示例 故去除约束 next step 结果有正有负合理 2.常见警告⚠ Structure contains HETATM entries. P…

SQL面试题及详细答案150道(61-80) --- 多表连接查询篇

《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。 前后端面试题-专栏总目录 文章目录 一、本文面试题目录 61. 什么是内连接(INNE…