Day3(网络编程)

UDP搭建tftp客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr, "line : __%d__\n", __LINE__);\
	perror(msg);\
	return-1;\
}while(0)

#define PORT 69
#define IP "192.168.31.186" 	//window ip地址


//函数声明
int do_Download(int sfd, struct sockaddr_in sin);
int do_UpData(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");
	}
	printf("套接字创建成功 sfd = %d\n", sfd);

	//填充结构体
	//明确知道是从服务端发送的,不需要在保存,可绑定可不绑定,一般不需要
	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.下载-------------\n");
		printf("--------2.上传-------------\n");
		printf("--------3.退出-------------\n");
		printf("---------------------------\n");
		printf("请输入>>> ");
		choose = getchar();
		while(getchar() != 10);

		switch(choose)
		{
			case '1':
				do_Download(sfd, sin);
				break;
			case '2':
				do_UpData(sfd, sin);
				break;
			case '3':
				goto END;
				break;
			default:
				printf("输入错误请重新输入:\n");
				break;
		}

	}
	
END:
	close(sfd);
	return 0;
}

int do_Download(int sfd, struct sockaddr_in sin)
{
	//发送请求包
	//从终端获取下载文件名
	char filename[32] = "";
	printf("请输入下载文件名:");
	fgets(filename, sizeof(filename), stdin);
	filename[strlen(filename)-1] = '\0';

	char buff[600] = "";

	//使用sprintf组装请求信息
	int ret = sprintf(buff, "%c%c%s%c%s%c", 0, 1,  filename, 0, "octet", 0);
	if(ret < 0)
	{
		ERR_MSG("sprintf");
	}
	
	//先给tftp服务器发送请求包
	if(-1 == sendto(sfd, buff, ret, 0, (struct sockaddr*)&sin, sizeof(sin)))
	{
		ERR_MSG("sendto");
	}
	printf("发送请求包成功\n");



	//构建结构体,保存服务器发过来的地址信息,也可以直接将sin覆盖使用
/*
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
*/
	//本地创建并打开要下载的文件
	int fd = -1; 	//必须初始化一个无意义的文件描述符,否则close可能会出问题
	ssize_t size = 0;
	socklen_t addrlen = sizeof(sin);
	//两字节
	unsigned short code = 0;//操作码
	unsigned short num = 1; //块编号
	
	//循环接收
	while(1)
	{
		bzero(buff, sizeof(buff));

		//接收数据包
		size = recvfrom(sfd, buff, sizeof(buff), 0, (struct sockaddr *)&sin, &addrlen);
		printf("接收数据包大小:%ld\n", size);
		if(-1 == size)
		{
			ERR_MSG("recvfrom");
		}

		//解析操作码
		code = ntohs(*(unsigned short *)buff);

		//解析块编号

		if(code == 3) 	//操作码等于3时,开始将数据包写入文件
		{
			if(num == ntohs(*(unsigned short *)(buff+2)))
			{
				num++;
				if(-1 == fd) //保证打开一次就行了
				{
					fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
					if(-1 == fd)
					{
						ERR_MSG("open");
					}
				}
				//将数据包写入文件中
				if(write(fd, buff+4, size-4) < 0)
				{
					ERR_MSG("write");
				}
				//发送ACK
				//组合ACK信息
				*(unsigned short *)buff = htons(4);
		//		*(unsigned short *)(buff + 2) = htons(num);

				if(-1 == sendto(sfd, buff, 4,  0, (struct sockaddr *)&sin, addrlen))
				{
					ERR_MSG("sendto");
				}

				//判断文件包是否传输完毕
				if(size < 516)
				{
					printf("传输完毕\n");
					break;
				}
			}
		}
		else if(5 == code) 	//操作码等于5时,接受的是错误包
		{
			printf("MSG_ERR : code[%d] msg[%s] __%d__\n",\
					code, buff+4, __LINE__);
			return -1;
		}
	}
	close(fd);
	return 0;
}

int do_UpData(int sfd,struct sockaddr_in sin)
{
	//上传文件
	char buff[516] = "";
	char filename[32] = "";

	bzero(buff, sizeof(buff));
	bzero(filename, sizeof(filename));

	printf("请输入上传的文件:");
	fgets(filename, sizeof(filename), stdin);
	filename[strlen(filename)-1] = '\0';

	//发送上传请求
	int ret = sprintf(buff, "%c%c%s%c%s%c", 0, 2,  filename, 0, "octet", 0);
	if(ret < 0)
	{
		ERR_MSG("sprintf");
	}
	//sendto
	if(sendto(sfd, buff, ret, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
	}

	//打开要上传的文件
	int fd = -1;

	socklen_t addrlen = sizeof(sin);

	ssize_t res = 0;
//	unsigned short code = 2; 	//记录操作码
	unsigned short num = 0; //记录块编码

	while(1)
	{
		bzero(buff, sizeof(buff));
		//接收文件
		if(recvfrom(sfd, buff, sizeof(buff), 0, (struct sockaddr*)&sin, &addrlen) < 0)
		{
			ERR_MSG("recvfrom");
		}
		printf("PORT:%d\n", sin.sin_port);
		printf("接收ACK code:[%d], num:[%d] __%d__\n", buff[1], buff[3], __LINE__);
		//循环发送文件并查看收到的文件
		//如果是ACK
		if(4 == buff[1])
		{
			//防止重复发送
			if(htons(num) == *(unsigned short*)(buff+2))
			{
				num++;

				//打开要上传文件
				if(-1 == fd)
				{
					fd = open(filename, O_RDONLY);
					if(fd < 0)
					{
						ERR_MSG("open");
					}
				}

				//读取目标文件
				if((res = read(fd, buff+4, 512)) < 0)
				{
					ERR_MSG("read");
				}
				printf("读取大小:%ld\n", res);

				//组回应数据包
				buff[1] = 3;
				*(unsigned short*)(buff+2) = htons(num);
				printf("回应数据包:code:[%d], num:[%d] __%d__\n", buff[1], buff[3], __LINE__);

				//发送文件
				if(sendto(sfd, buff, res+4, 0, (struct sockaddr*)&sin, addrlen) < 0)
				{
					ERR_MSG("sendto");
				}
				printf("发送成功\n");

				if(res < 512)
				{
					printf("文件上传完毕\n");
					break;
				}
			}
		}
		else if(5 == buff[1])
		{
			printf("MSG_ERR: code[%d], num[%d]错误退出\n", buff[1], buff[3]);
			break;
		}
	}

	//关闭fd
	close(fd);
	//关闭套接字
	close(sfd);

	return 0;
}

ubuntu@ubuntu:tftp$ gcc 01_UdpCli.c -o cli 
ubuntu@ubuntu:tftp$ ./cli 
套接字创建成功 sfd = 3
---------------------------
--------1.下载-------------
--------2.上传-------------
--------3.退出-------------
---------------------------
请输入>>> 2
请输入上传的文件:hello.text
PORT:2528
接收ACK code:[4], num:[0] __227__
读取大小:36
回应数据包:code:[3], num:[1] __257__
发送成功
文件上传完毕
---------------------------
--------1.下载-------------
--------2.上传-------------
--------3.退出-------------
---------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值