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;}

测试

在这里插入图片描述

相关内容

热门资讯

王凤英入职小鹏3年终获股权,此... 5月7日消息,小鹏汽车披露的监管及年报信息显示,公司总裁王凤英已正式进入股东名册,入职小鹏3年后股权...
五块钱红酒卖断货,便宜红酒为何... 最近一段时间,中国的酒类消费市场可以说是显得格外奇怪,一方面,各种高端酒特别是白酒的消费量出现了明显...
财联社C50风向指数调查:4月... 财联社5月8日讯(记者 夏淑媛)新一期财联社“C50风向指数”结果显示,市场机构对4月新增人民币贷款...
央视硬刚国际足联拒掏20亿,背... 作者| 史大郎&猫哥 来源| 是史大郎&大猫财经Pro 央视这次太刚了,离世界杯开幕还有1个月,死活...
新CEO上任直接放大招!Air... 快科技5月8日消息,苹果即将上任的CEO John Ternus对未来一系列新产品充满信心,称这些设...
“特朗普拟邀英伟达、波音等CE... 据路透社当地时间5月7日报道,特朗普政府正邀请英伟达、苹果、埃克森美孚、波音等大公司首席执行官,于下...
世界杯,还能看到直播吗? 2026年美加墨世界杯距离开幕,仅剩一个多月时间。多方信息显示,中央广播电视总台(以下简称“央视”)...
机构警告AI芯片热潮风险,超威... 5月7日,据央视财经,隔夜超威半导体公司(AMD)股价飙升近19%,带动AI芯片热潮持续升温。AMD...
银行员工转走储户1800万最新... 银行员工转走储户1800万最新进展:2名储户已收到银行全部款项
原创 中... 1994年,安徽省的经济格局曾发生过一次戏剧性的转折。在那一年,一座名为安庆的城市,其国内生产总值(...
昆都仑区:政策“蓄力”消费焕新 “一台5000多元的空调,叠加‘国补’和商场的以旧换新活动,能优惠1000元左右,旧机还能免费上门拆...
乐悦置业竞得佛山顺德乐从镇一商... 观点网讯:5月6日,佛山市顺德区乐从镇一商业地块成功出让,由广东省乐悦置业有限公司竞得,乐从南区·邻...
原创 亦... 《爱情没有神话》这部剧,一开始的命运颇为多舛,经历了几次撤档的波折后,终于在观众面前亮相,但其首播的...
美联储34年最大分歧叠加油价飙... 美联储按预期维持利率不变,但内部出现34年来最严重分歧,叠加布油创2022年6月以来新高,美债遭抛售...
支付宝消费券回收后,资金是否支... 摘要: 支付宝消费券回收变现后,资金能否直接转入信用卡?本文解答到账方式的相关规则,帮助用户了解资金...
中医介绍5个化痰穴位!收藏这篇... 很多人忽略了“痰”的危害,觉得咳几下就没事,殊不知,肺里的痰长期堆积,只会一步步加重身体负担。 中医...
黄金平台“杰我睿”涉嫌经济犯罪... 红星资本局5月7日消息,深圳水贝知名金店“杰我睿”兑付困难事件有了新进展。日前,深圳市公安局罗湖分局...
多地出台购房新政促楼市升温 记... 今年的“五一”假期,伴随着多个城市楼市新政密集落地,在叠加市场信心持续修复的作用下,房地产市场热度持...
谁是五一“吸金王”?这5座城市... 来源:市场资讯 (来源:21城市观) 哪座城市成为“五一”假期的大赢家? 图源:摄图网 作者|赵晓...
“低招低裁”格局稳固劳动力市场... 智通财经APP获悉,美国上周初请失业金人数在经历前一周回落至近几十年来最低水平后出现小幅反弹,表明尽...