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

测试

在这里插入图片描述

相关内容

热门资讯

雅江超级工程核心受益标的建材E... 受“雅江”1.2万亿超级工程利好催化,建材ETF(159745)今日开盘再度大涨近3%,昨日收盘也同...
刚一字涨停,又曝利好! 【导读】刚因雅下水电概念涨停,中国电建公告上半年水电新签合同额暴增66% 中国基金报记者 南深 7月...
银行板块短线跳水,厦门银行跌超... 银行板块短线跳水, 厦门银行跌超4%, 渝农商行跌超3%, 西安银行、 江苏银行、 重庆银行、 民生...
【网金基金研究中心】壹佰金每周... 壹佰金一周基金市场动态 1、核心资讯一览 Wind数据显示,截至7月18日17时,A股共有1540家...
1.25万亿份,净申购! 【导读】今年二季度基金整体净申购1.25万亿份,货基和债基为主力军 中国基金报记者 张燕北 公募二季...
骑士乳业及董事长党涌涛等被罚3... 具体来看,2024年,骑士乳业开展了豆粕、白糖、尿素等期货交易业务。截至2024年1月17日,骑士乳...
现货黄金突破3400美元关口 ... 财联社7月22日讯(编辑 牛占林)周一美盘交易时段,现货黄金突破3400美元/盎司,为6月17日以来...
摩根大通:人工智能和动量交易过... 市场中最具投机性的领域可能变得过于热门,且热度攀升速度过快。 摩根大通在周一发布的一份研究报告中警告...
“金融科技第一股”退市加速 记者丨曹媛 编辑丨孙超逸 “金融科技第一股”金融壹账通(6638.HK/OCFT.N)正加速退市。 ...
公募管理规模历史首破34万亿! 公募基金2025年二季报披露完毕。 天相投顾数据显示,公募基金二季度末管理规模历史首次超过34万亿元...
京东旗下首家自营外卖门店“七鲜... 观点网讯:7月21日消息,京东集团旗下首家自营外卖门店“七鲜小厨”已于7月20日在北京正式开业,标志...
企业居民融资成本处低位 7月L... 7月21日,中国人民银行授权全国银行间同业拆借中心公布,1年期贷款市场报价利率(LPR)为3.0%,...
港股“双重优势”吸引QDII基... 本报记者 彭衍菘 随着公募基金二季报陆续披露,QDII基金的区域配置策略调整引发市场关注。Wind资...
夯筑起应对复杂变局的坚实依托 安六高速铁路上的动车组列车驶过贵州省安顺市普定县化处镇。新华社记者 陶亮 摄 ...
“强实名”仍一票难求?遏制技术... 暑期来临,演唱会、音乐节、话剧等演出活动热度飙升。无论手速多快,总是一票难求,让众多消费者叫苦不迭。...
上证红利回报指数上涨0.83%... 金融界7月21日消息,上证指数高开高走,上证红利回报指数 (上红回报,H50019)上涨0.83%,...
为啥股票与基金的走势相反? 虚位以待! 平姐姐摄于毛里求斯网红酒店 昨天的文章,标题就很明确,那就是《准备出击》,在半年报不少上...
美加密货币相关法案落地引发三连... 当地时间7月18日,美国总统特朗普在白宫正式签署《指导与建立美国稳定币国家创新法案》(简称《天才法案...
股市必读:湖南黄金(00215... 截至2025年7月21日收盘,湖南黄金(002155)报收于18.33元,上涨2.57%,换手率3....
四川发布六大红色旅游新线路 四川发布六大红色旅游新线路 “锦绣天府·安逸四川”之红色旅游央地媒体联动采访启动 “锦绣天府·安...