网络编程day4

目录

基于UDP的TFTP文件上传

什么是网络字节序,为什么引入?

TCP服务器端通信流程的详细描述

TCP客户端通信流程的详细描述

什么是Nagle算法?

思维导图:


基于UDP的TFTP文件上传

代码:

#include<head.h>

//实现下载功能
int do_download(int cfd, struct sockaddr_in sin)
{
	//定义变量存储下载请求包
	char buf[516] = "";
	//定义变量存储文件名
	char fileName[40] = "";

	printf("请输入文件名:");
	scanf("%s", fileName);
	getchar();

	//组装请求包
	short *p1 = (short *)buf;
	*p1 = htons(1);                 //表明要下载

	char *p2 = buf+2;           //文件名段
	strcpy(p2, fileName);

	char *p4 = p2+strlen(p2)+1;    //模式段
	strcpy(p4, "octet");

	int size = 4 + strlen(p2) + strlen(p4);      //要发送数据的大小

	//向服务器发送下载请求
	if(sendto(cfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
	{
		perror("sendto error");
		return -1;
	}

	printf("请求成功\n");

	//循环接收回复服务器发来的消息
	int flag = 0;
	int fd=-1 ;

	//循环接收数据包
	int res=0;
	unsigned short num = 1; //块编号
	socklen_t addrlen = sizeof(sin);
	while(1)
	{   
		bzero(buf, sizeof(buf));                                                                                                      
		res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);
		if(res < 0)
		{  
			perror("recvfrom error");
			return -1; 
		}
		if(3 == buf[1])    //数据包
		{
			if(0 == flag)   //防止文件重复打开
			{
				//创建并打开filename文件                                                                                    
				fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0664);
				if(fd ==-1 )
				{
					perror("open error");
					return -1;
				}
				flag = 1;
			}
			//判断当前的块编号
			if(htons(num) == *(unsigned short*)(buf+2))     //防止数据包重复到达
			{
				//提取数据,写入到文件中

				if(write(fd, buf+4, res-4) < 0)                                                                        
				{
					break;
				}

				//回复ACK包将数据包的操作码修改成4,发送数据包的前四个字节。
				buf[1] = 4;
				sendto(cfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) ;


				//判断数据包的大小是否小于516
				if(res < 516)
				{
					printf("文件下载完成\n");
					break;
				}                                                                                                           
				num++; //块编号加1
			} 
		}
		else if(5 == buf[1])    //错误包
		{
			//打印错误信息
			printf("ERROR:%s\n", buf+4);
			break;
		}
	}
	close(fd);
	return 0;

}








//实现上传功能
int do_upload(int cfd, struct sockaddr_in sin)
{
	//定义变量存储文件名
	char fileName[40] = "";

	printf("请输入要上传的文件名:");
	scanf("%s", fileName);
	getchar();

	int fd=-1;
	fd=open(fileName,O_RDONLY);
	if(fd==-1)
	{
		perror("open error");
		return -1;
	}

	//定义变量存储上传请求包
	char buf[516] = "";

	//组装请求包
	short *p1 = (short *)buf;
	*p1 = htons(2);                 //表明要上传

	char *p2 = buf+2;           //文件名段
	strcpy(p2, fileName);

	char *p4 = p2+strlen(p2)+1;    //模式段
	strcpy(p4, "octet");

	int size = 4 + strlen(p2) + strlen(p4);      //要发送数据的大小

	//向服务器发送上传请求
	if(sendto(cfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
	{
		perror("sendto error");
		return -1;
	}

	printf("请求成功\n");

	//循环接收回复服务器发来的消息

	//循环发送数据包
	int res=0;
	unsigned short num = 0; //块编号
	socklen_t addrlen = sizeof(sin);
	while(1)
	{   
		bzero(buf, sizeof(buf));                                                                                                      
		res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &addrlen);
		if(res < 0)
		{  
			perror("recvfrom error");
			return -1; 
		}
		if(4 == buf[1])    //判断是否是ACK
		{
			//判断当前数据包的编号是否等于应答包的编号
			if(num == ntohs(*(unsigned short*)(buf+2)))
			{

				//修改操作码为数据包
				buf[1] = 3;

				//填充块编号
				num++;                                                                              
				*(unsigned short*)(buf+2) = htons(num);

				//发送数据

				res =read(fd,buf+4,512);

				if(res< 0)                                                                        
				{
					perror("read error");
					return -1;
				}else if(res==0)
				{
					printf("上传完成\n");
					break;
				}
				sendto(cfd, buf, res+4, 0, (struct sockaddr*)&sin, sizeof(sin)) ;
			} else
			{
				break;
			}
		}

		else if(5 == buf[1])    //错误包
		{
			//打印错误信息
			printf("ERROR:%s\n", buf+4);
			break;
		}
	}
	close(fd);
	return 0;

}





/*******************************主函数*********************/
int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("input error\n");
		printf("usage:./a.out ip\n");
		return -1;
	}

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

	//2、填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(69);
	sin.sin_addr.s_addr = inet_addr(argv[1]);

	int mune = -1;
	while(1)
	{
		system("clear");                     //清屏
		printf("\t\t======1、下载=======\n");
		printf("\t\t======2、上传=======\n");
		printf("\t\t======0、退出=======\n");

		printf("请输入功能:");
		scanf("%d", &mune);
		getchar();

		//多分支选择
		switch(mune)
		{
		case 1:
			{
				do_download(cfd, sin);
			}
			break;

		case 2:
			{

				do_upload(cfd,sin);
			}
			break;
		case 0:
			goto POS;
		default:printf("输入功能有误,请重新输入\n");
		}

		//阻塞
		printf("输入任意键,按回车清空:");
		while(getchar() != '\n');

	}

POS:
	//关闭套接字
	close(cfd);


	return 0;
}

效果图:

什么是网络字节序,为什么引入?

网络字节序是数据在不同主机之间传输的一种格式,采用大端存储。不同主机在存储多字节整数时,由于cpu内核架构不同,分为大端存储和小端存储,主机间通信时,由于大小端存储不同,收到的数据也可能不是想要的数据,对此,引入网络字节序。无论发送端是大端存储还是小端存储,数据发送到网络上时统一按网络字节序传输。接收端接收消息后,再根据自己的存储方式将网络字节序转换为本机字节序。

TCP服务器端通信流程的详细描述

1.创建套接字用于和客户端进行连接(socket)。

2.填充服务器地址信息结构体(地址族、本地地址和端口号),绑定套接字(bind)。

3.设置被动监听状态(listen)。

4.定义用于接收客户端地址信息的结构体变量,服务器接受连接请求(accept)。

5.和客户端进行数据传输(send/recv)。

6.关闭套接字(close)。

TCP客户端通信流程的详细描述

1.创建套接字用于和服务器进行连接和通信(socket)。

2.填充客户端地址信息结构体(地址族、本地地址和端口号),绑定套接字(bind)。(非必须)

3.填充服务器地址信息结构体,连接服务器。(connect)

4.和服务器进行数据传输(sendto/recvfrom)。

5.关闭套接字(close)。

什么是Nagle算法?

当应用程序产生少量的数据(例如1个字节),并且这些数据以数据包的形式发送到远端服务器时,就可能导致网络由于太多的数据包而过载。这种情况下,虽然只有1个字节的有效数据,但每个数据包却需要额外的40个字节的包头(IP头20字节+TCP头20字节)。如果发送端多次发送包含少量字符的数据包,Nagle算法会尝试将这些小包累积起来,直到它们达到一定的大小或者一定的时间后再一起发送,以减少网络拥塞的可能性。

思维导图:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值