TFTP协议文件传输

在这里插入图片描述
下载模型
在这里插入图片描述
上传模型
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>

#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);

int do_download(int client, struct sockaddr_in remote)
{
	//组下载请求包
	char buf[516] = {0};
	char filename[30] = "";
	puts("请输入要下载的文件名>>>>");
	scanf("%s", filename);
	while(getchar() != 10);

	//操作码
	unsigned short* cmd = (unsigned short*)buf;
	*cmd = htons(1);

	//文件名
	char* f_n = buf + 2;
	strcpy(f_n, filename);

	//模式
	char* mode = f_n + 1 + strlen(f_n);
	strcpy(mode, "octet");

	//请求长度
	int size = strlen(mode) + strlen(f_n) + 4;

	//发送下载请求 sendto
	socklen_t len = sizeof(remote);
	if(sendto(client, buf, size, 0, (struct sockaddr*)&remote, len) == -1)
	{
		LOG("sendto error");
		return -1;
	}

	int res = 0;
	unsigned short num = 0;			
	int fd_w = -1;

	while(1)
	{
		//接受数据
		bzero(buf, sizeof(buf));
		if((res = recvfrom(client, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len)) == -1)
		{
			LOG("recvfrom error");
			return -1;
		}

		//数据包接收
		if(3 == buf[1])
		{
			num++;
			//判断块编号是否合法
			if(*(unsigned short*)(buf+2) == htons(num))
			{
				//第一个数据包则创建文件
				if(1 == num)
				{
					if((fd_w = open("./5.png", O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
					{
						LOG("open error");
						return -1;
					}
				}
				if(write(fd_w, buf+4, res-4) == -1)
				{
					LOG("write error");
					return -1;
				}
			}

			//组ACK
			*cmd = htons(4);
			//发送ACK
			if(sendto(client, buf, sizeof(int), 0, (struct sockaddr*)&remote, len) == -1)
			{
				LOG("sendto error");
				return -1;
			}


			//判断数据是否小于512字节,小于则下载完成
			if(res - 4 < 512)
			{
				puts("下载完成");
				break;
			}
		}
		//错误码信息判断
		if(5 == buf[1])
		{
			fprintf(stderr, "错误码:%d,错误信息:%s\n", ntohs(*(unsigned short*)(buf+2)), buf+4);
			break;
		}


	}


	close(fd_w);

	return 0;
}

int do_upload(int client, struct sockaddr_in remote)
{
	//组写请求数据包
	char buf[516] = {0};
	char filename[30] = "";
	puts("请输入要上传的文件名称>>>>");
	scanf("%s", filename);
	while(getchar() != 10);


	//操作码
	unsigned short* cmd = (unsigned short*)buf;
	*cmd = htons(2);

	//文件名
	char* f_n = buf + 2;
	strcpy(f_n, filename);

	//模式
	char* mode = f_n + 1 + strlen(f_n);
	strcpy(mode, "octet");

	//请求长度
	int size = strlen(mode) + strlen(f_n) + 4;

	//发送下载请求 sendto
	socklen_t len = sizeof(remote);
	if(sendto(client, buf, size, 0, (struct sockaddr*)&remote, len) == -1)
	{
		LOG("sendto error");
		return -1;
	}

	int res = 0;//记录读文件的长度
	unsigned short num = 0;//记录块编码
	int fd_r = -1;//定义读文件描述符
	bzero(buf, sizeof(buf));
	//接收数据
	if((res = recvfrom(client, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len) == -1))
	{
		LOG("recvfrom error");
		return -1;
	}
	//判断是否为第0号ACK应答包
	if(buf[1] != 4 || *(unsigned short*)(buf+2) != htons(num))
	{
		puts("ACK包非法");
		return -1;
	}

	puts("服务器连接成功,正在上传。。。");

	//打开文件
	if((fd_r = open("./sly.jpg", O_RDONLY)) == -1)
	{
		LOG("open error");
		return -1;
	}


	while(1)
	{
		//判断ACK应答顺序是否合法
		if(buf[1] != 4 || *(unsigned short*)(buf+2) != htons(num))
		{
			puts("ACK包非法");
			return -1;
		}
		//数据包块编号自增
		num++;
		bzero(buf, sizeof(buf));
		//读文件
		if((res = read(fd_r, buf+4, 512)) == -1)
		{
			LOG("read error");
			return -1;
		}
		//给数据包的操作数和块编码赋值
		*cmd = htons(3);
		*(unsigned short*)(buf+2) = htons(num);

		//计算数据包长度
		size = res + 4;

		//发送数据包
		if(sendto(client, buf, size, 0, (struct sockaddr*)&remote, len) == -1)
		{
			LOG("sendto error");
			return -1;
		}

		//接收ACK应答包
		bzero(buf, sizeof(buf));
		if(recvfrom(client, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len) == -1)
		{
			LOG("recvfrom error");
			return -1;
		}


		//文件结束标志
		if(res < 512)
		{
			puts("上传成功");
			break;
		}

		//错误码信息判断
		if(5 == buf[1])
		{
			fprintf(stderr, "错误码:%d,错误信息:%s\n", ntohs(*(unsigned short*)(buf+2)), buf+4);
			break;
		}
	}
	close(fd_r);

	return 0;
}

int main(int argc, char *argv[])
{

	//创建套接字
	int client = 0;
	if((client = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	{
		LOG("socket error");
		return -1;
	}

	//地址信息结构体赋值
	struct sockaddr_in remote = {0};

	remote.sin_family = AF_INET;
	remote.sin_port = htons(69);
	remote.sin_addr.s_addr = inet_addr("192.168.114.227");	

	char choose = 0;
	while(1)
	{
		system("clear");
		printf("-----------------------\n");
		printf("---------1. 下载-------\n");
		printf("---------2. 上传-------\n");
		printf("---------3. 退出-------\n");
		printf("-----------------------\n");
		printf("请输入>>>>");
		scanf("%c", &choose);
		while(getchar() != 10);

		switch(choose)
		{
		case '1':
			do_download(client, remote);
			break;

		case '2':
			do_upload(client, remote);
			break;

		case '3':
			goto END;
			break;

		default:
			puts("输入有误,重新输入");
			break;
		}

		puts("输入任意字符清屏>>>");
		while(getchar() != 10);

	}




END:
	//关闭文件描述符
	close(client);

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值