电子产业一站式赋能平台

PCB联盟网

搜索
查看: 57|回复: 0
收起左侧

基于TCP实现文件传输与UDP编程方法

[复制链接]

278

主题

278

帖子

1796

积分

三级会员

Rank: 3Rank: 3

积分
1796
发表于 2024-9-30 11:45:00 | 显示全部楼层 |阅读模式
关注+星标公众,不错过精彩内容

32nfmt0uhgr64030634808.jpg

32nfmt0uhgr64030634808.jpg

直接来源 | 瑞萨嵌入式小百科
TCP/IP通信协议我们做项目编程的时候,经常都会用到,它的底层原理其实有点复杂,但我们实际编程应用的时候,主要都是调用一些接口,了解了这些接口的原理,实现起来也不是很难。
今天就结合源码讲讲基于TCP实现文件传输与UDP编程方法。
基于TCP协议,实现文件传输。约定规则:先传输一个结构体,里面含有“文件名,文件大小”
1
Client:
  • open文件,得到文件状态(大小),构造结构体,发送结构体。
  • 循环read文件,发送网络数据。
    [/ol]
    2
    Server:
  • 读网络数据,得到结构体,创建文件。
  • 循环read网络数据,写文件,数量足够就关闭文件。
    [/ol]
    获取文件信息

    本节源码位于如下目录:

    23rneqem2xa64030634908.png

    23rneqem2xa64030634908.png


    关键代码:
    左右滑动查看完整内容
  • /* ./file_stat 1.txt */int main(int argc, char **argv){ struct stat statbuf; if (argc != 2) { printf("Usage: %s
    ", argv[0]); return -1; } /* 1. open */ int fd = open(argv[1], O_RDONLY); if (-1 == fd) { printf("can't open %s
    ", argv[1]); return -1; } /* 2. fstat */ if (fstat(fd, &statbuf) == -1) { printf("can't get stat of %s
    ", argv[1]); return -1; }           /* 3. printf */        printf("file size = %d
    ", statbuf.st_size);         return 0;        }
    实验方法:
  • gcc -o file_stat file_stat.cecho 123 > 1.txt./file_stat 1.txt

    本地读文件、写文件

    本节源码位于如下目录:


    关键代码:
    左右滑动查看完整内容
  • /* 4. 创建目标文件 */ int fd2 = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC); if (-1 == fd2) { printf("can't create %s
    ", argv[2]); return -1; }  while (1) { /* 读原文件 */ unsigned char c; if (read(fd, &c, 1) != 1) break; /* 写目标文件 */ if (write(fd2, &c, 1) != 1) break; }
    上机实验:
    左右滑动查看完整内容
  • 编译程序: gcc -o mycp mycp.c生成大的测试文件:dd if=/dev/zero of=tmp.txt bs=1024 count=10240测试:date ; ./mycp tmp.txt tmp6.txt ; date
    实现网络传输:传输文件信息

    本节源码位于如下目录:
  • 06_源码\8.4.2_本地读文件、写文件

    cgwmpzllxsd64030635008.png

    cgwmpzllxsd64030635008.png


    源码为“netcp1_info”

    netcp程序:
    左右滑动查看完整内容
  • 28 int main(int argc, char **argv)29 {30 int iSocketClient;31 struct sockaddr_in tSocketServerAddr;3233 int iRet;34 unsigned char ucSendBuf[1000];35 int iSendLen;3637 if (argc != 4)38 {39 printf("Usage:
    ");40 printf("%s   
    ", argv[0]);41 return -1;42 }4344 iSocketClient = socket(AF_INET, SOCK_STREAM, 0);// 创建一个套接字4546 tSocketServerAddr.sin_family = AF_INET;47 tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short*/48 //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;49 if (0 == inet_aton(argv[2], &tSocketServerAddr.sin_addr))50 {51 printf("invalid server_ip
    ");52 return -1;53 }54 memset(tSocketServerAddr.sin_zero, 0, 8);555657 iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));// 尝试连接到服务器58 if (-1 == iRet)59 {60 printf("connect error!
    ");61 return -1;62 }6364 /* 获得文件信息 */65 struct stat statbuf;6667 int fd = open(argv[1], O_RDONLY);// 打开第一个参数指定的本地文件68 if (-1 == fd)69 {70 printf("can't open %s
    ", argv[1]);71 return -1;72 }7374 if (fstat(fd, &statbuf) == -1)// 获取文件信息(大小)75 {76 printf("can't get stat of %s
    ", argv[1]);77 return -1;78 }7980 /* 发送文件信息 */81 struct netcp_info info;82 info.len = statbuf.st_size;83 strcpy(info.name, argv[3]);84 //将 info 结构发送到服务器85 iSendLen = send(iSocketClient, &info, sizeof(info), 0);86 if (iSendLen 0)87 {88 close(iSocketClient);89 return -1;90 }91 else92 {93 printf("send %d datas, %ld
    ", iSendLen, sizeof(info));94 }959697 return 0;98 }
    server程序:
    左右滑动查看完整内容
  • 43 signal(SIGCHLD,SIG_IGN);// 忽略子进程结束信号,防止出现僵尸进程。4445 iSocketServer = socket(AF_INET, SOCK_STREAM, 0);//创建一个 IPv4 的 TCP 套接字46 if (-1 == iSocketServer)47 {48 printf("socket error!
    ");49 return -1;50 }51 //结构体设置为接受任何 IP 的连接请求52 tSocketServerAddr.sin_family = AF_INET;53 tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short*/54 tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;55 memset(tSocketServerAddr.sin_zero, 0, 8);5657 iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));// 将套接字与特定的 IP 地址和端口号绑定58 if (-1 == iRet)59 {60 printf("bind error!
    ");61 return -1;62 }6364 iRet = listen(iSocketServer, BACKLOG);// 函数使服务器的套接字进入监听状态65 if (-1 == iRet)66 {67 printf("listen error!
    ");68 return -1;69 }7071 while (1) //服务器将持续运行,等待客户端的连接请求72 {73 iAddrLen = sizeof(struct sockaddr);74 iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);// 等待客户端的连接请求,并创建一个新的套接字用于与客户端通信75 if (-1 != iSocketClient)76 {77 iClientNum++;78 printf("Get connect from client %d : %s
    ", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));79 if (!fork())//创建子进程来处理客户端请求,父进程继续监听其他客户端的连接请求80 {81 /* 读 100 字节的文件信息 */82 struct netcp_info info;83 //收客户端发送的文件信息,包括文件长度和文件名84 iRecvLen = recv(iSocketClient, &info, sizeof(info), 0);85 if (iRecvLen 0)86 {87 close(iSocketClient);88 return -1;89 }90 else91 {92 printf("Get file info From Client %d: file len =%ld, file name = %s
    ", iClientNum, info.len, info.name);93 }9495 }96 }97 }
    编译:
    左右滑动查看完整内容
  • source /opt/remi-sdk/environment-setup-aarch64-poky-linux$CC netcp.c -o netcp$CC server.c -o server
    假设设置开发板的IP为:192.168.5.9,上传程序到开发板上。
  • scp ./server root@192.168.5.9:/mnt/scp ./netcp root@192.168.5.9:/mnt/
    测试:
    进入/mnt目录运行程序:
  • root@myir-remi-1g:~# cd /mnt/root@myir-remi-1g:/mnt# lsserver netcp
    在运行程序前,请先创建txt文件,写入任意数据至文件中,用于后续文件传输。
    左右滑动查看完整内容
  • root@myir-remi-1g:/mnt# touch 1.txtroot@myir-remi-1g:/mnt# echo 123 > 1.txt
    后台运行服务程序,运行客户端程序进行文件信息传输。
    左右滑动查看完整内容
  • root@myir-remi-1g:/mnt# ./server &root@myir-remi-1g:/mnt# ./netcp 1.txt 127.0.0.1 2.txtGet connect from client 0 : 127.0.0.1send 104 datas, 104Get file info From Client 0: file len = 4, file name = 2.txt
    运行程序后,可以看到终端输出文件信息内容。

    注意:如果修改server.c,再次编译、运行前,先关闭之前的sever程序。命令为:killall server。

    实现网络传输:传输文件数据

    本节源码位于如下目录:

    xhiwoazxpfd64030635108.png

    xhiwoazxpfd64030635108.png


    源码为“netcp2_info”

    netcp程序:
    左右滑动查看完整内容
  • 80 /* 发送文件信息 */81 struct netcp_info info;82 info.len = statbuf.st_size;83 strcpy(info.name, argv[3]);8485 iSendLen = send(iSocketClient, &info, sizeof(info), 0);//发送文件信息86 if (iSendLen 0)87 {88 close(iSocketClient);89 return -1;90 }91 else92 {93 printf("send %d datas, %ld
    ", iSendLen, sizeof(info));94 }9596 /* 传输数据 */97 unsigned char buf[1024];98 uint64_t total_len = 0;99 while (1)100 {101 /* 读原文件 */102 //unsigned char c;103 //if (read(fd, &c, 1) != 1)104 // break;105 int len;106 len = read(fd, buf, 1024);107 if (len 0)108 break;109 else110 {111 /* 通过网络发送给服务器 */112 iSendLen = send(iSocketClient, buf, len, 0);113 if (iSendLen != len)114 {115 close(iSocketClient);116 return -1;117 }118 //printf("send ok %d
    ", iSendLen);119120 }121 }122 return 0;123 }
    server程序:
    左右滑动查看完整内容
  • 72 while (1)73 {74 iAddrLen = sizeof(struct sockaddr);75 iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);76 if (-1 != iSocketClient)77 {78 iClientNum++;79 printf("Get connect from client %d : %s
    ", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));80 if (!fork())81 {82 /* 读 100 字节的文件信息 */83 struct netcp_info info;8485 iRecvLen = recv(iSocketClient, &info, sizeof(info), 0);86 if (iRecvLen 0)87 {88 close(iSocketClient);89 return -1;90 }91 else92 {93 printf("Get file info From Client %d: file len =%ld, file name = %s
    ", iClientNum, info.len, info.name);94 }9596 /* 创建文件 */97 int fd = open(info.name, O_CREAT|O_WRONLY|O_TRUNC, 0666);98 if (-1 == fd)99 {100 printf("can't create %s
    ", info.name);101 return -1;102 }103 /* 接收网络数据,写文件 */104 unsigned char buf[1024];105 uint64_t writelen = 0;106107 while (1)108 {109 iRecvLen = recv(iSocketClient, buf, 1024, 0);110 if (iRecvLen 0)111 {112 close(iSocketClient);113 return -1;114 }115 else116 {117 if (write(fd, buf, iRecvLen) != iRecvLen)118 {119 printf("can not write file
    ");120 return -1;121 }122 writelen += iRecvLen;123 if (writelen == info.len)124 {125 /* 接收到所有数据并且成功写入 */126 printf("get file %s ok
    ", info.name);127 close(fd);128 return 0;129 }
    编译:
    左右滑动查看完整内容
  • source /opt/remi-sdk/environment-setup-aarch64-poky-linux$CC netcp.c -o netcp$CC server.c -o server
    假设设置开发板的IP为:192.168.5.9,上传程序到开发板上。

    测试:
    进入/mnt目录运行程序:
  • root@myir-remi-1g:~# cd /mnt/root@myir-remi-1g:/mnt# lsserver netcp
    在运行程序前,请先创建txt文件,写入任意数据至文件中,用于后续文件传输。
    左右滑动查看完整内容
  • root@myir-remi-1g:/mnt# touch 1.txtroot@myir-remi-1g:/mnt# echo 123 > 1.txt
    后台运行服务程序,运行客户端程序进行文件信息传输。
    左右滑动查看完整内容
  • root@myir-remi-1g:/mnt# ./server &root@myir-remi-1g:/mnt# ./netcp 1.txt 127.0.0.1 2.txtsend 104 datas, 104Get connect from client 0 : 127.0.0.1Get file info From Client 0: file len = 4, file name = 2.txtget file 2.txt ok
    运行程序后,可以看到终端输出文件数据获取成功。

    注意:如果修改server.c,再次编译、运行前,先关闭之前的sever程序。命令为:killall server。


    UDP编程示例
    本节源码位于如下目录:

    k0ehmwum2zi64030635208.png

    k0ehmwum2zi64030635208.png


    Client程序:
    左右滑动查看完整内容
  • 34 iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);3536 tSocketServerAddr.sin_family = AF_INET;37 tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short*/38 //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;39 if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))40 {41 printf("invalid server_ip
    ");42 return -1;43 }44 memset(tSocketServerAddr.sin_zero, 0, 8);4546 #if 047 iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));48 if (-1 == iRet)49 {50 printf("connect error!
    ");51 return -1;52 }53 #endif5455 while (1)56 {57 if (fgets(ucSendBuf, 999, stdin)) //从标准输入读取用户输入的数据。58 {59 #if 060 iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);61 #else62 iAddrLen = sizeof(struct sockaddr);63 iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf),0,64 (const struct sockaddr *)&tSocketServerAddr, iAddrLen);// 将数据通过 UDP 发送给服务器
    server程序:
    左右滑动查看完整内容
  • 45 iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); //将套接字与特定的 IP 地址和端口号绑定46 if (-1 == iRet)47 {48 printf("bind error!
    ");49 return -1;50 }515253 while (1) //服务器将持续运行,等待客户端的消息54 {55 iAddrLen = sizeof(struct sockaddr);56 iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr*)&tSocketClientAddr, &iAddrLen);// 接收客户端发送的消息57 if (iRecvLen > 0)58 {59 ucRecvBuf[iRecvLen] = '\0';60 printf("Get Msg From %s : %s
    ", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);61 }62 }6364 close(iSocketServer);65 return 0;66 }
    编译:
    左右滑动查看完整内容
  • source /opt/remi-sdk/environment-setup-aarch64-poky-linux$CC netcp.c -o netcp$CC client.c -o client
    假设设置开发板的IP为:192.168.5.9,上传程序到开发板上。
  • scp ./server root@192.168.5.9:/mnt/scp ./client root@192.168.5.9:/mnt/
    测试:
    进入/mnt目录运行程序:
  • root@myir-remi-1g:~# cd /mnt/root@myir-remi-1g:/mnt# lsserver netcp
    后台运行服务程序,运行客户端程序进行文件信息传输。
    左右滑动查看完整内容
  • root@myir-remi-1g:/mnt# ./server &root@myir-remi-1g:/mnt# ./client 127.0.0.1nihaoGet Msg From 127.0.0.1 : nihao
    运行程序后,可以在终端输入信息后,可以看到服务端打印接收到的信息。

    ------------ END ------------

    k3ydzzto3wu64030635308.gif

    k3ydzzto3wu64030635308.gif

    ●瑞萨RA8系列教程 | 初识瑞萨 RA8 系列单片机●瑞萨RA8系列教程 | 瑞萨 RA8 开发环境搭建●瑞萨RA8系列教程 | 基于 Keil 开发 RA8单片机瑞萨RA8系列教程 | 基于e2s实现RA8串口输出配置
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表