网络编程第三天

创建tftp客户端

运行结果:

网络编程第三天_第1张图片

网络编程第三天_第2张图片

tftp协议

网络编程第三天_第3张图片

网络编程第三天_第4张图片

tftp下载模型

网络编程第三天_第5张图片

代码:

#include 

#define PORT 69				 //tftp服务器端口
#define IP "192.168.170.131" //tftp服务器运行环境的IP

int do_download(int cfd, struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success cfd = %d __%d__\n", cfd, __LINE__);

	//填充服务器的地址信息结构体,AT_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;			 //必须为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() != '\n')
			; //循环吸收垃圾字符

		switch (choose)
		{
		case '1':
			// 下载
			do_download(cfd, sin);
			break;
		case '2':
			// 上传
			do_upload(cfd, sin);
			break;
		case '3':
			// 退出
			exit(0);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	}

	//关闭所有文件描述符
	if (close(cfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

// 下载
int do_download(int cfd, struct sockaddr_in sin)
{
	//组下载请求包
	char buf[516] = "";
	char filename[20] = "";
	printf("请输入要下载的文件名>>>");
	scanf("%s", filename);
	getchar();

	//ptr1指向操作码
	short *ptr1 = (short *)buf;
	*ptr1 = htons(1);
	char *ptr2 = (char *)(ptr1 + 1);
	strcpy(ptr2, filename);
	char *ptr3 = ptr2 + strlen(ptr2) + 1;
	strcpy(ptr3, "octet");
	size_t len = 2 + strlen(ptr2) + 1 + strlen(ptr3) + 1;

	//接收数据的字节数
	ssize_t rlen = 0;

	//组ACK包
	char ack_buf[4] = "";
	short *ptr4 = (short *)ack_buf;
	*ptr4 = htons(4);
	short *ptr5;

	//发送下载请求给tftp服务器 sendto
	if (sendto(cfd, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}

	//打开要下载的文件,没有就创建文件
	int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0777);
	if (fd < 0)
	{
		ERR_MSG("open");
		return -1;
	}

	//接收服务器的IP和端口
	struct sockaddr_in rcvadd;
	int rcvlen;

	while (1)
	{
		bzero(buf, sizeof(buf));
		//接收服务器传回来的数据包 recvfrom
		rlen = recvfrom(cfd, buf, 516, 0, (struct sockaddr *)&rcvadd, &rcvlen);
		if (rlen < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}

		//如果是ERROR包
		if (*ptr1 == htons(5))
		{
			fprintf(stderr, "错误码:%d, %s\n", ntohs(*(ptr1 + 1)), (char *)(ptr1 + 2));
			return -1;
		}

		//将收到的数据写入文件
		if (write(fd, buf + 4, rlen - 4) < 0)
		{
			ERR_MSG("write");
			return -1;
		}

		//组ACK包
		ptr5 = (short *)(buf + 2);
		*(ptr4 + 1) = *ptr5;

		//回发应答包到tftp服务器 sendto
		if (sendto(cfd, ack_buf, 4, 0, (struct sockaddr *)&rcvadd, rcvlen) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}

		if (rlen < 516)
		{
			printf("下载完毕\n");
			break;
		}
	}

	// 关闭文件描述符
	if (close(fd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
}

// 上传
int do_upload(int cfd, struct sockaddr_in sin)
{
	//组上传请求包
	char buf[516] = "";
	char filename[20] = "";
	// 块编号
	short kid = 1;
	printf("请输入要上传的文件名>>>");
	scanf("%s", filename);
	getchar();

	//ptr1指向操作码
	short *ptr1 = (short *)buf;
	*ptr1 = htons(2);
	char *ptr2 = (char *)(ptr1 + 1);
	strcpy(ptr2, filename);
	char *ptr3 = ptr2 + strlen(ptr2) + 1;
	strcpy(ptr3, "octet");
	size_t len = 2 + strlen(ptr2) + 1 + strlen(ptr3) + 1;

	//读取文件要发送数据的字节数
	ssize_t slen = 0;

	// ACK包
	char ack_buf[128] = "";
	// 指向操作码
	short *ptr4 = (short *)ack_buf;
	// *ptr4 = htons(4);

	//发送上传请求给tftp服务器 sendto
	if (sendto(cfd, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	bzero(buf, sizeof(buf));
	sleep(1);

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

	//接收要发送的服务器的IP和端口
	struct sockaddr_in sndadd;
	int sndlen;
	int res;

	while (1)
	{
		//接收服务器传回来的ACK包 recvfrom
		res = recvfrom(cfd, ack_buf, 128, 0, (struct sockaddr *)&sndadd, &sndlen);
		if (res < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}

		//如果是ERROR包
		if (*ptr4 == htons(5))
		{
			fprintf(stderr, "错误码:%d, %s\n", ntohs(*(ptr4 + 1)), (char *)(ptr4 + 2));
			return -1;
		}

		// 读取ACK包
		if (*ptr4 == htons(4))
		{
			if (kid != ntohs(*(ptr4 + 1)) + 1)
			{
				// 发送上一次的数据包
				if (sendto(cfd, buf, 4 + slen, 0, (struct sockaddr *)&sndadd, sndlen) < 0)
				{
					ERR_MSG("sendto");
					return -1;
				}
				continue;
			}
		}

		// 组数据包
		*ptr1 = htons(3);
		*(ptr1 + 1) = htons(kid);

		// 从文件中读取数据
		slen = read(fd, buf + 4, 512);
		if (slen < 0)
		{
			ERR_MSG("read");
			return -1;
		}

		//发送数据包到tftp服务器 sendto
		if (sendto(cfd, buf, 4 + slen, 0, (struct sockaddr *)&sndadd, sndlen) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		kid++;

		if (slen < 512)
		{
			printf("上传完毕\n");
			break;
		}
	}

	// 最后还有一个ACK应答包
	res = recvfrom(cfd, ack_buf, 128, 0, (struct sockaddr *)&sndadd, &sndlen);
	if (res < 0)
	{
		ERR_MSG("recvfrom");
		return -1;
	}

	// 关闭文件描述符
	if (close(fd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
}

你可能感兴趣的:(网络编程,网络,服务器,c语言,linux)