【网络编程】Day3 UDP

 1. 将UDP的服务器客户端重新搭建

服务器端代码:

#include 

/*--------------------UDP 服务器端---------------------*/
#define ERR_MSG(msg) do{\
				fprintf(stderr,"_%d_",__LINE__);\
				perror(msg);\
				return -1;\
			}while(0)

#define IP "192.168.0.105"  //查看本机IP地址 ifconfig
#define PORT 7777

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket error");
	}
	printf("sfd = %d\n",sfd);


	//2. 填充服务器的地址信息结构体 AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;       //IPv4
	sin.sin_port        = htons(PORT);   //端口号
	sin.sin_addr.s_addr = inet_addr(IP); //IP地址


	//3. 将套接字和网络信息结构体绑定
	if(-1 == bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
	{
		ERR_MSG("bind error");
	}
	printf("bind success\n");

	//4.接收,发送收据
	char buf[128]="";
	int res=0;
	struct sockaddr_in cin;  //用来存储客户端数据包
	socklen_t addrlen = sizeof(cin);
	while(1)
	{
		//接收收据
		memset(buf,0,sizeof(buf));
		res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);  //注意没有新的文件描述符产生
		if(-1 == res)
		{
			ERR_MSG("recv error");
		}
		printf("接收到来自[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
		
		//发送数据
		strcat(buf,"***");
		if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,sizeof(cin)))
		{
			ERR_MSG("sendto error");
		}
		printf("发送信息到[%s : %d] :%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);	
	}
	
	//5.关闭套接字
	close(sfd);

	return 0;
}

客户端代码:

#include 

/*--------------------UDP 客户端---------------------*/
#define ERR_MSG(msg) do{\
				fprintf(stderr,"_%d_",__LINE__);\
				perror(msg);\
				return -1;\
			}while(0)

#define IP "192.168.0.105"  //服务器端 IP地址 
#define PORT 7777   //服务器端 端口号

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);   //UDP协议: SOCK_DGRAM
	if(sfd < 0)
	{
		ERR_MSG("socket error");
	}
	printf("sfd = %d\n",sfd);

	//绑定客户端的地址信息--->非必须绑定
	//若不绑定则会自动绑定本机IP以及随机端口
	

	//2. 填充要发送的服务器的地址信息结构体,给下面的sendto函数使用
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;       //必须填IPv4
	sin.sin_port        = htons(PORT);   //服务器绑定的端口号(网络字节序)
	sin.sin_addr.s_addr = inet_addr(IP); //服务器绑定的IP地址(网络字节序)

	//3.接收,发送收据
	char buf[128]="";
	ssize_t res=0;

	struct sockaddr_in rcvaddr;  //存储数据包
	socklen_t addrlen = sizeof(rcvaddr);

	while(1)
	{	
		//发送数据
		memset(buf,0,sizeof(buf));  //缓存清0
		printf("请输入>>>");    	//通过终端输入
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1] = '\0';

		if(-1 == sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin)))
		{
			ERR_MSG("sendto error");
		}
		printf("发送信息到[%s : %d] :%s\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),buf);	
		
		//接收收据
		memset(buf,0,sizeof(buf));
		if(-1 == recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&rcvaddr,&addrlen))
	//	if(-1 == recvfrom(sfd,buf,sizeof(buf),0,NULL,NULL))
	//	if(-1 == recv(sfd,buf,sizeof(buf),0))
	//	if(-1 == read(sfd,buf,sizeof(buf)))
		{
			ERR_MSG("recv error");
		}
		printf("接收到来自[%s : %d] :%s\n",inet_ntoa(rcvaddr.sin_addr),ntohs(rcvaddr.sin_port),buf);
	}
	
	//4.关闭套接字
	close(sfd);

	return 0;
}

运行结果如下:

ubuntu@ubuntu:01udp$ ./ser.c 
sfd = 3
bind success
接收到来自[192.168.0.105 : 40788] :hello
发送信息到[192.168.0.105 : 40788] :hello***
接收到来自[192.168.0.105 : 40788] :summer
发送信息到[192.168.0.105 : 40788] :summer***
ubuntu@ubuntu:01udp$ ./cli.c 
sfd = 3
请输入>>>hello
发送信息到[192.168.0.105 : 7777] :hello
接收到来自[192.168.0.105 : 7777] :hello***
请输入>>>summer
发送信息到[192.168.0.105 : 7777] :summer
接收到来自[192.168.0.105 : 7777] :summer***
请输入>>>

2.编写tftp的下载代码

代码如下:

#include 

/*===============================基于UDP 的tftp客户端===========================*/
#define ERR_MSG(msg) do{\
				fprintf(stderr,"_%d_",__LINE__);\
				perror(msg);\
				return -1;\
			}while(0)

#define IP "192.168.0.104"  //查看服务器的IP地址 windows下ipconfig
#define PORT 69   //tftp专用

int download(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 error");
	}

	//绑定客户端的地址信息-->非必须绑定
	//若不绑定则会自动绑定本机IP以及随机端口
	

	//2. 填充服务器的地址信息结构体,给下面的sendto函数使用
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;       //
	sin.sin_port        = htons(PORT);   //端口号
	sin.sin_addr.s_addr = inet_addr(IP); //IP地址



	char c = 0;
	while(1)
	{
		printf("\n----------------------------------\n");
		printf("               1.上传             \n");
		printf("               2.下载             \n");
		printf("               3.退出             \n");
		printf("********************************* \n");
		printf("请选择>>> ");

		c = getchar();
		while(getchar() != 10);

		switch(c)
		{
			case '1':  //上传

				break;
			case '2':  //下载
				download(sfd,sin);
				break;
			case '3':  //退出
				goto END;
			default:
				printf("输入有误,请重新输入\n");
		}

	}

END:
	//5.关闭套接字
	close(sfd);

	return 0;
}

int download(int sfd,struct sockaddr_in sin)
{
	char filename[30] = "";
	printf("请输入文件名>>>");
	scanf("%s",filename);
	while(getchar() != 10);

//组下载请求协议包
	//操作码           文件名         \0             模式           \0
	//2Byte       n Bytes String     1Byte      n Bytes String     1Byte
	char buf[516] = "";

	unsigned short *p1 = (unsigned short*)buf;
	*p1 = htons(1);  //p1操作码
	
	char *p2 = buf+2;
	strcpy(p2,filename);

	char *p3 =p2+strlen(filename);
	*p3 = 0;
	
	char *p4 = p3+1;
	strcpy(p4,"octet");

	int size = 4+strlen(p2)+strlen(p4);

	//向服务器发送下载请求
	if(-1 == sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)))
	{
		ERR_MSG("sendto error");
	}
	printf("发送下载请求成功\n");

	unsigned short code=0;   //操作码
	unsigned short block=0;  //块编号
	char data[512]="";  //数据
	int count=0;
	int len=0;
	int fd;
	unsigned short *p = p1+1;   //指向块编号
	socklen_t addrlen = sizeof(sin);

	while(1)
	{
		//接收数据包
		if(-1 == (len=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)))  //需接收新的端口号(临时端口号12345)
		{
			ERR_MSG("recv error");
		}

		//解析接收到的数据包
		code = ntohs(*p1);   	//解析操作码
		block = ntohs(*p);  	//解析块编号
		strcpy(data,buf+4);  	//解析数据

		if(code == 3 && block==count+1)   //下载数据校验
		{
			count++;
			if(count==1)
			{
				fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
				if(-1 == fd)
				{
					ERR_MSG("file open error");
				}
			}
			if(-1 == write(fd,data,len-4))
			{
				ERR_MSG("write error");
			}

		//回复ACK
			*p1 = htons(4);   //ACK操作码
			*p  = htons(block);   //ACK块编号
			if(-1 == sendto(sfd,buf,4,0,(struct sockaddr*)&sin,addrlen))
			{
				ERR_MSG("sendto error");
			}

		//判断数据是否小于512,若<512,则传输结束
			if(len-4 < 512)
			{
				close(fd);
				printf("已完成下载\n");
				break;
			}

		}

	}
}

运行结果如下:

ubuntu@ubuntu:day3$ gcc 05udp_tftp.c 
ubuntu@ubuntu:day3$ ./a.out 

----------------------------------
               1.上传             
               2.下载             
               3.退出             
********************************* 
请选择>>> 2
请输入文件名>>>1_udpSer.c
发送下载请求成功
已完成下载

----------------------------------
               1.上传             
               2.下载             
               3.退出             
********************************* 
请选择>>> 3
ubuntu@ubuntu:day3$ 

你可能感兴趣的:(udp,网络协议)