9.12 TFTP通信

客户端设计(仅供参考):

下载本质:读取服务器发送的数据包,写入到本地文件

上传本质:读取本地文件内容,发送给服务器。

1、建立菜单选项,上传和下载。

2、上传功能函数:

1、只读打开本地文件

2、发送上传请求数据包

3、接收服务器应答并回复ACK包

3、1如果接收的是ACK确认包,将包修改为数据包填充本地文件内容并发送给服务器

3、2如果是错误包,上传结束,检查网络。

3、下载功能函数

1、发送下载请求包

2、循环接收服务器数据包

2.1判断收到的包如果是数据包并且块编号正确,就写入客户端文件,然后回复ACK确认包

2.2如果包的长度小于512+2+2,说明接收完毕

2.3如果是错误包。输出对应错误信息并停止接收。

TFTP下载模型:

TFTP上传模型

TFTP通信过程总结

c 复制代码
1、服务器在69号端口等待客户端的请求
2、服务器若批准此请求,则使用临时端口与客户端进行通信
3、每个数据包的编号都有变化(从1开始)
4、每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数据或
ACK)
5、数据的长度以512Byte传输
6、小于512Byte的数据意味着传输结束

do_upload(上传文件)

csharp 复制代码
void do_upload(int sockfd,struct sockaddr_in sin,const char *filename){
	char text[1024] = "";
	int text_len;
	socklen_t sinlen = sizeof(sin);
	int fd;
	int flags = 0;
	int num = 0;
	ssize_t bytes;
	text_len = sprintf(text,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
	if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
		perror("sendto");
		exit(1);
	}
	while(1){
		if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
			perror("recvfrom");
			exit(1);
		}
		printf("接收操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));

		if(text[1] == 5){
			printf("差错码是:%d,差错信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
			return ;
		}
		else if(text[1] == 4){
			if(flags == 0){
				if((fd = open(filename,O_RDONLY))<0){
					perror("open");
					exit(1);
				}
				flags = 1;
				printf("文件打开成功:%d\n",fd);
			}
			int len = read(fd,text+4,512);
			if (len < 0) { //检查 read 函数的返回值
                perror("read");
                exit(1);
            }
		//	printf("%s\n",text+4);
			if(len == 0){
				break;
			}
			text[1] = 3;
			*(short *)(text+2) = htons(++num);
			printf("发送操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
			if(sendto(sockfd,text,len+4,0,(struct sockaddr *)&sin,sinlen)<0){
				perror("sendto");
				exit(1);
			}
		}
	}
}

do_download(下载文件)

csharp 复制代码
void do_download(int sockfd,struct sockaddr_in sin,const char *filename){
	char text[1024] = "";
	int text_len;
	socklen_t sinlen = sizeof(sin);
	int fd;
	int flags = 0;
	int num = 0;
	ssize_t bytes;
	
	text_len = sprintf(text,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
	if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
		perror("sendto");
		exit(1);
	}
	while(1){
		if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
			perror("recvfrom");
			exit(1);
		}
		printf("操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));

		if(text[1] == 5){
			printf("错误码是:%d,错误信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
			return;
		}
		else if(text[1]==3){
			if(flags==0){
				if((fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664))<0){
					perror("open");
					exit(1);
				}
				flags = 1;
			}
			if((num+1 == ntohs(*(short *)(text+2))) && (bytes == 516)){
				num = ntohs(*(short *)(text+2));
				if(write(fd,text+4,bytes-4)<0){
					perror("write");
					exit(1);
				}
				text[1] = 4;
				if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
					perror("sendto");
					exit(1);
				}
			}
			else if((num+1 == ntohs(*(short *)(text+2)))&&(bytes<516)){
				if(write(fd,text+4,bytes-4)<0){
					perror("write");
					exit(1);
				}
				text[1]=4;
				if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
					perror("sendto");
					exit(1);
				}
				printf("文件下载完毕\n");
				return;
			}
		}
	}
	close(fd);
}

运行结果




完整代码

c 复制代码
#include <myhead.h>
#define SERPORT 69
#define SERIP "192.168.11.1"

void do_download(int sockfd,struct sockaddr_in sin,const char *filename);
void do_upload(int sockfd,struct sockaddr_in sin,const char *filename);

int main(int argc, const char *argv[])
{
    //1.创建用于通信的套接字文件描述符
    int clienfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (clienfd == -1) {
        perror("socket");
        return -1;
    }
    //2.填充服务器的地址信息结构体
    struct sockaddr_in sin = {
        .sin_family = AF_INET,
        .sin_port = htons(SERPORT),
        .sin_addr.s_addr = inet_addr(SERIP)
    };
    //3.菜单栏选择
    int choice;
    char filename[100];
    while (1) { //3.菜单栏
        printf("请选择操作:\n");
        printf("1.上传文件\n");
        printf("2.下载文件\n");
        printf("3.退出\n");
        scanf("%d", &choice);
        bzero(filename, sizeof(filename));
        if (choice == 1) {
            printf("请输入要上传的文件名:");
            scanf("%s", filename);
            do_upload(clienfd, sin, filename);
        } else if (choice == 2) {
            printf("请输入要下载的文件名:");
            scanf("%s", filename);
            do_download(clienfd, sin, filename);
        } else if (choice == 3) {
            break;
        } else {
            printf("无效选项,请重新选择。\n");
        }
    }
    close(clienfd);
    return 0;
}

void do_download(int sockfd,struct sockaddr_in sin,const char *filename){
	char text[1024] = "";
	int text_len;
	socklen_t sinlen = sizeof(sin);
	int fd;
	int flags = 0;
	int num = 0;
	ssize_t bytes;
	
	text_len = sprintf(text,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
	if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
		perror("sendto");
		exit(1);
	}
	while(1){
		if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
			perror("recvfrom");
			exit(1);
		}
		printf("操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));

		if(text[1] == 5){
			printf("错误码是:%d,错误信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
			return;
		}
		else if(text[1]==3){
			if(flags==0){
				if((fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664))<0){
					perror("open");
					exit(1);
				}
				flags = 1;
			}
			if((num+1 == ntohs(*(short *)(text+2))) && (bytes == 516)){
				num = ntohs(*(short *)(text+2));
				if(write(fd,text+4,bytes-4)<0){
					perror("write");
					exit(1);
				}
				text[1] = 4;
				if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
					perror("sendto");
					exit(1);
				}
			}
			else if((num+1 == ntohs(*(short *)(text+2)))&&(bytes<516)){
				if(write(fd,text+4,bytes-4)<0){
					perror("write");
					exit(1);
				}
				text[1]=4;
				if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
					perror("sendto");
					exit(1);
				}
				printf("文件下载完毕\n");
				return;
			}
		}
	}
	close(fd);
}

void do_upload(int sockfd,struct sockaddr_in sin,const char *filename){
	char text[1024] = "";
	int text_len;
	socklen_t sinlen = sizeof(sin);
	int fd;
	int flags = 0;
	int num = 0;
	ssize_t bytes;
	text_len = sprintf(text,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
	if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
		perror("sendto");
		exit(1);
	}
	while(1){
		if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
			perror("recvfrom");
			exit(1);
		}
		printf("接收操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));

		if(text[1] == 5){
			printf("差错码是:%d,差错信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
			return ;
		}
		else if(text[1] == 4){
			if(flags == 0){
				if((fd = open(filename,O_RDONLY))<0){
					perror("open");
					exit(1);
				}
				flags = 1;
				printf("文件打开成功:%d\n",fd);
			}
			int len = read(fd,text+4,512);
			if (len < 0) { //检查 read 函数的返回值
                perror("read");
                exit(1);
            }
		//	printf("%s\n",text+4);
			if(len == 0){
				break;
			}
			text[1] = 3;
			*(short *)(text+2) = htons(++num);
			printf("发送操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
			if(sendto(sockfd,text,len+4,0,(struct sockaddr *)&sin,sinlen)<0){
				perror("sendto");
				exit(1);
			}
		}
	}
}
相关推荐
Tim风声(网络工程师)1 小时前
不同射频对应不同mac地址(查找无线用户连接AP信息)
服务器·网络·tcp/ip·智能路由器·无线ap
ajassi20002 小时前
开源 C++ QT Widget 开发(十五)多媒体--音频播放
linux·c++·qt·开源
JosieBook3 小时前
【远程运维】Linux 远程连接 Windows 好用的软件:MobaXterm 实战指南
linux·运维·windows
文档搬运工3 小时前
Linux MInt启动速度的优化
linux
Broken Arrows4 小时前
Linux学习——管理网络安全(二十一)
linux·学习·web安全
索迪迈科技5 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育
Light605 小时前
领码方案|Linux 下 PLT → PDF 转换服务超级完整版:异步、权限、进度
linux·pdf·可观测性·异步队列·plt转pdf·权限治理·进度查询
鳄鱼杆5 小时前
服务器 | Docker应用开发与部署的实践以及阿里云镜像加速配置
服务器·阿里云·docker
羚羊角uou5 小时前
【Linux】命名管道
linux·运维·服务器
IT 小阿姨(数据库)5 小时前
PgSQL监控死元组和自动清理状态的SQL语句执行报错ERROR: division by zero原因分析和解决方法
linux·运维·数据库·sql·postgresql·centos