admin 管理员组文章数量: 1087580
实现简单的TFTP服务器的下载与上传功能
1、作业要求
在Linux系统中使用C语言代码,实现简单的TFTP服务器的下载与上传功能。
2、实现过程
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>#define ERR_MSG(msg) do{\fprintf(stderr, "line:%d\n", __LINE__);\perror(msg);\
}while(0)#define IP "192.168.8.231" //windows IP地址
#define PORT 69 //tftp服务器的端口号int do_download(int sfd, struct sockaddr_in sin);
int do_upload(int sfd, struct sockaddr_in sin);int main(int argc, const char *argv[])
{//创建报式套接字int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(sfd < 0){ERR_MSG("socket");return -1;}//填充服务器自身的地址信息结构体,AF_INET: man 7 IP//供于下方的sendto使用,因为sendto必须要指明发给谁struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.s_addr = inet_addr(IP);char choose = 0;while(1){printf("-----------------------\n");printf("------1.download-------\n");printf("------2.upload---------\n");printf("------3.exit-----------\n");printf("-----------------------\n");printf("请输入对用的序号>>>");choose = getchar();while(getchar()!=10);switch(choose){case '1': do_download(sfd, sin);break;case '2':do_upload(sfd, sin);break;case '3':goto END;break;default:printf("输入错误,请重新输入\n\n");//continue;break;}}END://关闭套接字close(sfd);return 0;
}//下载文件
int do_download(int sfd, struct sockaddr_in sin)
{//发送下载请求char buf[516] = "";ssize_t res = 0;char filename[50] = "";printf("请输入要下载的文件名>>>");scanf("%s", filename);while(getchar()!=10);int size = sprintf(buf, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0);//发送下载协议if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}//打开一个文件,用于存储下载到的数据int fd=-1, flag = 0;socklen_t addrlen = sizeof(sin);unsigned short num = 0;while(1){bzero(buf, sizeof(buf));//接收数据包res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);if(res < 0){ERR_MSG("recvfrom");return -1;}// printf("buf[0]:%d buf[1]:%d 操作码:%#x\n", buf[0], buf[1], ntohs(*(short*)buf));//由于操作码为网络字节序,所以有效操作码的数据应该存储在buf[1]的位置//所以只需要判断buf[1]是3 还是 5即可if(3 == buf[1]) //数据包{if(0 == flag){fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);if(fd < 0){ERR_MSG("open");return -1;}flag = 1;}//UDP是无连接的不可靠的,数据包可能会重复接收到的通信;//可以在本地记录每次收到的包的编号,//如果本地记录的包的编号与数据包发送回来的快编号不一致,则不做处理if(htons(num+1) == *(unsigned short*)(buf+2)){//将数据保存到文件中,//最后一次下载到的数据肯定小于512,所以不能直接写512if(write(fd, buf+4, res-4) < 0){ERR_MSG("write");break;}//回复ACKbuf[1] = 4;if(sendto(sfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");break;}//判断数据是否<512 if(res-4 < 512){printf("文件 %s 下载完毕\n\n", filename);break;}num++;}}else if(5 == buf[1]) //错误包{printf("ERROR: %d %s\n", ntohs(*(short*)(buf+2)), buf+4);break;}}//关闭文件close(fd);return 0;
}//上传文件
int do_upload(int sfd, struct sockaddr_in sin){char buf[516] = "";ssize_t res = 0;char filename[50] = "";printf("请输入要上传的文件名>>>");scanf("%s",filename);while(getchar()!=10);//判断文件是否存在int fd = open(filename, O_RDONLY);if(fd<0){ERR_MSG("open");return -1;}int size = sprintf(buf, "%c%c%s%c%s%c", 0, 2, filename, 0, "octet", 0);//发送上传请求,69号端口if(sendto(sfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin))<0){ERR_MSG("sendto");return -1;}socklen_t addrlen = sizeof(sin);unsigned short num = 0;size_t res_read =0;while(1){//接收数据包res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);if(res<0){ERR_MSG("recvfrom");return -1;}//printf("buf[1]= %d 操作码:%d 块编号:%d\n",buf[1], ntohs(*(short*)buf), ntohs(*(short*)(buf+2)));//组数据包,数据包块编号从1开始,数据内容从文件中读取512个字节if(4 == buf[1]) //校验ACK{//UDP是无连接的不可靠的,数据包可能会重复接收发送;//可以在本地记录每次收到的包的编号,//如果本地记录的包的编号与数据包发送回来的块编号不一致,则不做处理//第一次接收到的ACK的块编号为0,if(htons(num) == *(unsigned short*)(buf+2)){//将文件中的数据读取出来,//最后一次上传的的数据肯定小于512,所以不能直接写512//组数据包num++;bzero(buf, sizeof(buf));*(short*)buf = htons(3);*(short*)(buf+2) = htons(num);res_read = read(fd, buf+4, 512);if(res_read<0){ERR_MSG("read");break;}//上传数据包if(sendto(sfd, buf, res_read+4, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");break;}//判断数据是否<512 if(res_read < 512){printf("文件 %s 上传完毕\n\n", filename);break;}}}}//关闭文件close(fd);
}
3、实现效果
本文标签: 实现简单的TFTP服务器的下载与上传功能
版权声明:本文标题:实现简单的TFTP服务器的下载与上传功能 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1700324253a397131.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论