Linux应用程序通过 tcp/udp实现文件传输
创始人
2025-06-01 01:31:08
0

基础知识

socket编程——socket_in结构体

sockaddr_in在头文件#include或#include 中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:
在这里插入图片描述
在这里插入图片描述
参数说明
sin_family主要用于定义是地址族
sin_port主要用来保存端口号
sin_addr主要用来保存IP地址信息
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。

socklen_t类型

socklen_t是一种数据类型,和int差不多,在32位机下,size_t和int的长度相同,都是32 bits,但在64位机下,size_t(32bits)和int(64 bits)的长度是不一样的,socket编程中的accept函数的第三个参数的长度必须和int的长度相同。于是有了socklen_t类型。

名词解释

网络字节顺序 (Network Byte Order) NBO
结构体的sin_port和sin_addr都必须是NBO
本机字节顺序 (Host Byte Order) HBO
一般可视化的数字都是HBO

NBO,HBO二者转换
inet_addr() 将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton() 将字符串点数格式地址转化成NBO
inet_ntoa () 将NBO地址转化成字符串点数格式
htons() “Host to Network Short”
htonl() “Host to Network Long”
ntohs() “Network to Host Short”
ntohl() “Network to Host Long”
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型

htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。

inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。

inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。

TCP/UDP网络通信大概交互图

在这里插入图片描述

UDP用户数据包模式

在这里插入图片描述

报错解决

在测试过程中遇到以下报错:
Connection reset by peer
TCP链接中常见名词是Client Server, 但是网络连接中经常出现Connection reset by peer
在这里插入图片描述
报错分析:
client:客户端
server: 服务端
peer: tcp端。
peer是脱离固定场景的, tcp的任意一端都叫peer.
客户端收到Connection reset by peer代表服务端关闭了链接
服务端收到Connection reset by peer代表客户端关闭了链接

关闭链接的原因可能性就比较多了:
防火墙,路由器等。
解决方法:重启服务端端口


tcp

tcp服务端代码

//tcp-server
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define N 128#define ERRLOG(errmsg) do{\perror(errmsg);\printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\exit(-1);\}while(0)typedef struct __MSG{char buff[N];int bytes;
}msg_t;int main(int argc, const char *argv[]){if(2 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//创建服务器网络信息结构体struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[1]));server_addr.sin_addr.s_addr = htonl(INADDR_ANY);socklen_t addrlen = sizeof(server_addr);//3.将套接字和网络信息结构体进行绑定if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){ERRLOG("bind error");}//4.将服务器的套接字设置成被动监听状态if(-1 == listen(sockfd, 5)){ERRLOG("listen error");}//定义一个结构体,保存客户端的信息struct sockaddr_in client_addr;memset(&server_addr, 0, sizeof(client_addr));//清空socklen_t clientaddrlen = sizeof(client_addr);char buff[N] = {0};int acceptfd = 0;
ACCEPT://5.阻塞等待客户端连接acceptfd = accept(sockfd, (struct sockaddr *)&client_addr, &clientaddrlen);if(-1 == acceptfd){ERRLOG("accept error");}printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));RENAME:if(-1 == recv(acceptfd, buff, N, 0)){ERRLOG("recv error");}printf("客户端要下载的文件名为:[%s]\n", buff);int fd = open(buff, O_RDONLY);if(-1 == fd){if(errno == ENOENT){printf("文件[%s]不存在\n",buff);if(-1 == send(acceptfd, "****NO EXIST****", N, 0)){perror("send error");goto RENAME;}}else{ERRLOG("open error");}}printf("文件[%s]存在\n",buff);if(-1 == send(acceptfd, "****EXIST****", N, 0)){ERRLOG("send error");}int bytes = 0;msg_t msg;memset(&msg, 0, sizeof(msg));//循环读取文件内容并发送给客户端while((bytes = read(fd, msg.buff, N))>0){msg.bytes = bytes;if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){ERRLOG("send error");}memset(&msg, 0, sizeof(msg));}//发送传输完毕的信息strcpy(msg.buff, "****OVER****");msg.bytes = 0;if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){ERRLOG("send error");}close(acceptfd);goto ACCEPT;close(sockfd);return 0;
}

tcp客户端代码

//tcp-cliaent
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define N 128#define ERRLOG(errmsg) do{\perror(errmsg);\printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\exit(-1);\}while(0)typedef struct __MSG{char buff[N];int bytes;
}msg_t;int main(int argc, const char *argv[]){if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//创建服务器网络信息结构体struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[2]));server_addr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t addrlen = sizeof(server_addr);//与服务器建立连接if(-1 == connect(sockfd, (struct sockaddr *)&server_addr, addrlen)){ERRLOG("connect error");}char filename[N] = {0};char buff[N] = {0};int fd = 0;
RENAME:printf("请输入要下载的文件:");scanf("%s", filename);//将要下载的文件发送给服务器if(-1 == send(sockfd, filename, N, 0)){ERRLOG("send error");}//接收文件是否存在的信息if(-1 == recv(sockfd, buff, N, 0)){ERRLOG("recv error");}if(0 == strcmp(buff, "****NO EXIST****")){printf("文件不存在\n");goto RENAME;}else if(0 == strcmp(buff, "****EXIST****")){//创建并打开并清空一个文件,准备下载文件内容if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){ERRLOG("open error");}}int bytes = 0;msg_t msg;memset(&msg, 0, sizeof(msg));while(recv(sockfd, &msg, sizeof(msg), 0)>0){if(msg.bytes == 0){break;}if(-1 == write(fd, msg.buff, msg.bytes)){ERRLOG("write error");}memset(&msg, 0, sizeof(msg));}close(fd);printf("文件下载完成\n");//关闭套接字close(sockfd);return 0;
}

测试

在这里插入图片描述

udp

前言:由于udp是不可靠连接,在传输文件的过程中不能保证文件的完整性,该demo只是实现了udp文件传输,不保证准确性

udp服务端

//udp-server
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define N 512#define ERRLOG(errmsg) do{\perror(errmsg);\printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\exit(-1);\}while(0)typedef struct __MSG{char buff[N];int bytes;
}msg_t;int main(int argc, const char *argv[]){off_t count=0, m,sz;//longlong int n;char buff[N];int acceptfd = 0;if(2 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}//1.创建用户数据报式套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}//创建服务器网络信息结构体struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[1]));server_addr.sin_addr.s_addr = htonl(INADDR_ANY);socklen_t addrlen = sizeof(server_addr);//3.将套接字和网络信息结构体进行绑定if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){ERRLOG("bind error");}//定义一个结构体,保存客户端的信息struct sockaddr_in client_addr;memset(&server_addr, 0, sizeof(client_addr));//清空socklen_t clientaddrlen = sizeof(client_addr);#if 0 while(1){//接收数据,如果想要给对方回应,就必须保存对方的网络信息结构体//如果不回应,后两个参数写 NULL 也行if(-1 == recvfrom(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, &clientaddrlen)){ERRLOG("recvfrom error");}printf("%s(%d):%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);//组装应答信息strcat(buff, "--server");if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, clientaddrlen)){ERRLOG("sendto error");}memset(buff, 0, N);}close(sockfd);#endifACCEPT://5.阻塞等待客户端连接acceptfd = recvfrom(sockfd,buff,N,0, (struct sockaddr *)&client_addr, &clientaddrlen);if(-1 == acceptfd ){ERRLOG("recvfrom error");}printf("客户端 %s:%d 连接到服务器了\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));RENAME:printf("客户端要下载的文件名为:[%s]\n", buff);int fd = open(buff, O_RDONLY);if(-1 == fd){if(errno == ENOENT){printf("file [%s] inexistence\n",buff);if(-1 == sendto(sockfd, "****NO EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){perror("send error");goto RENAME;}}else{ERRLOG("open error");}}printf("file [%s] exist\n",buff);if(-1 == sendto(sockfd, "****EXIST****", N, 0,(struct sockaddr *)&client_addr, clientaddrlen)){ERRLOG("send error");}int bytes = 0;msg_t msg;memset(&msg, 0, N);n=read(fd,buff,N);printf("sending.....\n");while(n){if(n == -1){ERRLOG("read fails");}m=sendto(sockfd,buff,N,0,(struct sockaddr*)&client_addr,clientaddrlen);if(m==-1){ERRLOG("send error");}count+=m;bzero(buff,N);n=read(fd,buff,N);}msg.bytes = 0;printf("send over\n");m=sendto(sockfd,buff,0,0,(struct sockaddr*)&client_addr,clientaddrlen);printf("The number of bytes transferred : %lld\n",count);/* //循环读取文件内容并发送给客户端while((bytes = read(fd, msg.buff, N))>0){msg.bytes = bytes;if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){ERRLOG("send error");}else{}memset(&msg, 0, sizeof(msg));}//发送传输完毕的信息strcpy(msg.buff, "****OVER****");msg.bytes = 0;if(-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&client_addr, clientaddrlen)){ERRLOG("send error");} *///close(acceptfd);//goto ACCEPT;close(sockfd);close(fd);return 0;
}

udp客户端代码

//udp-client
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define N 512#define ERRLOG(errmsg) do{\perror(errmsg);\printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\exit(-1);\}while(0)typedef struct __MSG{char buff[N];int bytes;
}msg_t;int main(int argc, const char *argv[]){off_t count=0, n; // long typechar filename[N];char buff[N];int fd = 0;if(3 != argc){printf("Usage : %s  \n", argv[0]);exit(-1);}int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){ERRLOG("socket error");}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[2]));server_addr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t addrlen = sizeof(server_addr);#if 0 while(1){printf("input your msg:");fgets(buff, N, stdin);buff[strlen(buff)-1] = '\0';//清除 \nif(0 == strcmp(buff, "quit")){break;}if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&server_addr, addrlen)){ERRLOG("sendto error");}if(-1 == recvfrom(sockfd, buff, N, 0, NULL, NULL)){ERRLOG("recvfrom error");}printf("recv:[%s]\n", buff);memset(buff, 0, N);}//关闭套接字close(sockfd);
#endifRENAME:printf("Please enter the file you want to download:");scanf("%s", filename);if(-1 == sendto(sockfd, filename, N, 0,(struct sockaddr *)&server_addr, addrlen)){ERRLOG("send error");}if(-1 == recvfrom(sockfd, buff, N, 0,(struct sockaddr *)&server_addr, &addrlen)){ERRLOG("recv error");}if(0 == strcmp(buff, "****NO EXIST****")){printf("file does not exist\n");goto RENAME;}else if(0 == strcmp(buff, "****EXIST****")){if(-1 == (fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664))){ERRLOG("open error");}}// int bytes = 0;//msg_t msg;bzero(&buff,N);n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);printf("start receiving.......\n");while(n){if(n==-1){ERRLOG("read fails");}count+=n;write(fd,buff,n);bzero(buff,N);n=recvfrom(sockfd,&buff,N,0,(struct sockaddr *)&server_addr,&addrlen);}printf("file download completes\n");printf("The number of bytes receive : %lld\n",count);/*  memset(&msg, 0, sizeof(msg));while(recvfrom(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&server_addr, &addrlen)>0){if(msg.bytes == 0){break;}if(-1 == write(fd, msg.buff, msg.bytes)){ERRLOG("write error");}memset(&msg, 0, sizeof(msg));} */close(fd);close(sockfd);return 0;}

测试

在这里插入图片描述

相关内容

热门资讯

智谱成为全球大模型第一股,外国... 出品|虎嗅科技组 作者|赵致格 编辑|苗正卿 头图|视觉中国 1月8日,北京智谱华章科技股份有限公司...
埃克森石油公司研究重返委内瑞拉... 来源:环球市场播报 埃克森美孚首席执行官达伦-伍兹(Darren Woods)周五表示,美国石油巨头...
原创 特... 一个数据可以翻篇,也可以揭露一场政治与经济的戏码,2025年10月美国对外贸易数字一下子给出了一个让...
五千亿赛道争夺战:中式汉堡如何... 当塔斯汀在2025年以“手擀现烤中国汉堡”的定位闯入万店俱乐部时,国内汉堡市场的竞争格局已然改写。这...
原创 稀... 稀土“卖成白菜价”的那些年,其实一点都不风光 很多人第一次听到“稀土比铜还便宜”,都会下意识觉得不合...
开年大涨,2026黄金还将“狂... 2026年开年首个交易周结束,黄金价格再现大涨。 北京时间1月9日晚,在美国公布12月非农数据后,蓄...
原创 中... 特朗普政府在2026年初推动了一项针对俄罗斯能源出口的立法行动,明确将中国、印度、巴西等七个国家列为...
马斯克:白领劳动力将最先消失,... 大象新闻2026-01-10 08:38:28 近日,马斯克在播客节目中表示,AI将率先取代白领岗位...
场景化体验式 北京全时活跃消费... 1月10日至11日,全国商务工作会议在京召开,其中明确提出加快培育服务消费新增长点、释放服务消费潜力...
突发,603359或终止重大资... 东珠生态或终止重大资产重组。 1月9日盘后,东珠生态(603359)发布公告,此前公司筹划以发行股...
上海AI企业密集上市,关键在这... 最近上海的AI企业跟踩了“加速器”似的——24天里5家产业链公司成功上市,尤其是“GPU四小龙”(沐...
原创 囤... 囤货潮席卷全球?从油价到金价暴涨,普通人该躲坑还是布局?这篇说透底层逻辑 前几天去超市,发现货架上的...
酒店RWA新模型,让消费者变投... 酒店RWA数字化权益解决方案解析 一、应用场景 适用于传统酒店行业,尤其是面临重资产投入大、资金沉淀...
数百名女性和未成年人受害!被指... 印尼与马来西亚日前相继宣布,暂时封禁美国企业家埃隆·马斯克旗下的人工智能聊天机器人“格罗克(Grok...
文达通IPO辅导进展:募投项目... 乐居财经 李兰1月8日,青岛文达通科技股份有限公司(以下简称:文达通)发布关于公司向不特定合格投资者...
闫学晶发文致歉:我讨厌那个下意... 近日,演员闫学晶在直播中透露儿子拍一部戏“就挣几十万块钱”,需要负担家庭年开支百八十万,否则家庭无法...
湾财周报 | 人物 郁亮退休;... 一周人物(2025年1月5日-11日) 头条 郁亮到龄退休,35年万科生涯结束! 2026...
投资笔记:科技无法改变的、选准... 今天忙里偷闲看了一期雪球周刊,发现大家思考的范围都很广,同时自己也是受到了颇多启发和感悟。 【什么是...
非农喜忧参半 美联储降息概率全... 2025年12月美国非农就业报告让市场人士对美联储本月末降息的预期彻底落空。虽然2025年12月新增...
中辉期货天津营业部因内控问题被... 天津证监局近日发布公告,因居间人管理、员工管理等内部控制存在严重不足,中辉期货天津营业部被采取责令改...