41、网络编程/TCP.UDP通信模型练习20240301

news/2024/5/18 13:38:38 标签: tcp/ip, udp, 网络协议

一、编写基于TCP的客户端实现以下功能:

通过键盘按键控制机械臂:w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂

1.基于TCP服务器的机械臂,端口号是8888, ip是Windows的ip;

查看Windows的IP:按住Windows+r 按键,输入cmd , 输入ipconfig。

2.点击软件中的开启监听;

3.机械臂需要发送16进制数,共5个字节,协议如下

0xff    0x02    x   y   0xff
0xff:起始结束协议,固定的;
0x02:控制机械手臂协议,固定的;
x:指定要操作的机械臂
    0x00 红色摆臂
    0x01 蓝色摆臂
y:指定角度

代码:

#include<myhead.h>
#define SER_IP "192.168.125.64"  //服务器IP
#define SER_PORT 8888          //服务器端口号
int main(int argc, const char *argv[])
{
	//创建套接字文件
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd==-1)
	{
		perror("socket error");
		return -1;
	}
	//定义结构体存储服务器地址
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(SER_PORT);
	sin.sin_addr.s_addr=inet_addr(SER_IP);
	
	//连接服务器
	if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("connect error");
		return -1;
	}
	puts("connect success");

	//数据发送
	//定义控制红色臂的数组并初始化,范围是[-90,90]
	char rbuf[5]={0xff,0x02,0x00,0x00,0xff};
	//定义控制蓝色臂的数组并初始化,范围是[0,180]
	unsigned char bbuf[5]={0xff,0x02,0x01,0x00,0xff};
	
	//发送初始值
	send(sfd,rbuf,sizeof(rbuf),0);
	sleep(1);//防止沾包
	send(sfd,bbuf,sizeof(bbuf),0);

	char key=0;
	while(1)
	{
		system("stty -icanon");//关闭缓冲区
		key=getchar();
		fflush(stdin);
		switch(key)
		{
		case 'W':
		case 'w':
			{
				rbuf[3]+=2;//每次操作的角度偏移2度
				if(rbuf[3]>=90)
				{
					rbuf[3]=90;
				}
				send(sfd,rbuf,sizeof(rbuf),0);
			}break;
		case 'S':
		case 's':
			{
				rbuf[3]-=2;
				if(rbuf[3]<=-90)
				{
					rbuf[3]=-90;
				}
				send(sfd,rbuf,sizeof(rbuf),0);
			}break;
		case 'D':
		case 'd':
			{
				bbuf[3]+=2;
				if(bbuf[3]>=180)
				{
					bbuf[3]=180;
				}
				send(sfd,bbuf,sizeof(bbuf),0);
			}break;
		case 'A':
		case 'a':
			{
				bbuf[3]-=2;
				if(bbuf[3]>180)//无符号整数小于零后会向255循环
				{
					bbuf[3]=0;
				}
				send(sfd,bbuf,sizeof(bbuf),0);
			}break;
		}
	}
	close(sfd);
	return 0;
}

运行:

思维导图

二、 基于UDP的TFTP文件传输

1.tftp协议概述

简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2.tftp下载模型

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

差错码:

0 未定义,差错错误信息

1 File not found.

2 Access violation.

3 Disk full or allocation exceeded.

4 illegal TFTP operation.

5 Unknown transfer ID.

6 File already exists.

7 No such user.

8 Unsupported option(s) requested.

代码:

#include<myhead.h>
#define SER_IP "192.168.125.57"
#define SER_POTR 69
int my_download(int cfd,struct sockaddr_in sin);
int my_upload(int cfd,struct sockaddr_in sin);
int my_save_data(char *p,int len);
int recv_ack(int cfd,struct sockaddr_in sin);

int main(int argc, const char *argv[])
{
	//创建通信套接字
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd==-1)
	{
		perror("socket error");
		return -1;
	}
	//定义地址结构体 保存客户端地址
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(SER_POTR);
	sin.sin_addr.s_addr=inet_addr(SER_IP);
	int menu=0;
	while(1)
	{
		system("clear");
		puts("\t\t------请选择选项------");
		printf("\t\t-------1.上传-------\n");
		printf("\t\t-------2.下载-------\n");
		printf("\t\t-------0.退出-------\n");
		scanf("%d",&menu);
		getchar();
		switch(menu)
		{
			case 0:goto END;
			case 1:
				   {
					   int res=my_upload(cfd,sin);
					   if(res==-1)
					   {
						   puts("my_upload error");
						   return -1;
					   }
				   }break;
			case 2:
				   {
					   int res=my_download(cfd,sin);
					   if(res==-1)
					   {
						   puts("my_download error");
						   return -1;
					   }
				   }break;
			default:
				   {
				   		puts("输入有误,请重新输入");
				   }break;
		}
		puts("请输入任意键按回车结束!");
		while(getchar()!='\n');
	}

END:
	close(cfd);
	return 0;
}

int my_download(int cfd,struct sockaddr_in sin)
{
	//定义收发数据容器
	char pack[516]="";
	//组建请求协议包
	//1.请求下载
	printf("请输入要下载的文件名:");
	char txt[32]="";
	fgets(txt,sizeof(txt),stdin);
	txt[strlen(txt)-1]=0;
	short *p1=(short*)pack;
	*p1=htons(1);//存入前两字节的操作码1代表读(下载)
	char *p2=pack+2;
	strcpy(p2,txt);//存入文件名及结尾的0
	char *p3=p2+strlen(p2)+1;
	strcpy(p3,"octet");//存入模式位及结尾的0
	int packlen=4+strlen(p2)+strlen(p3);
	//发送下载请求
	if(sendto(cfd,pack,packlen,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("download request sendto error");
		return -1;
	}
/*	//读取服务器的回复消息
	int ack=recv_ack(cfd,sin);
	if(ack==-1)
	{
		puts("recv_ack error");
		return -1;
	}
*/
	while(1)
	{
		bzero(pack,sizeof(pack));
		int ack=recv_ack(cfd,sin);
		if(ack==-1)
		{
			puts("my_download recv_ack error");
			return -1;
		}else if(ack==0)//下载完成
		{
			break;
		}
		*p1=htons(4);//设置ACK包
		*(p1+1)=htons(ack);
		//返回ACK包
		if(sendto(cfd,pack,sizeof(pack),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
		{
			perror("upload request sendto error");
			return -1;
		}	
	}
	return 0;
}
int my_upload(int cfd,struct sockaddr_in sin)
{
	//定义收发数据容器
	char pack[516]="";
	//组建请求协议包
	//1.请求写入
	printf("请输入要上传的文件名:");
	char txt[32]="";
	fgets(txt,sizeof(txt),stdin);
	txt[strlen(txt)-1]=0;
	short *p1=(short*)pack;
	*p1=htons(2);//存入前两字节的操作码2代表写(上传)
	char *p2=pack+2;
	strcpy(p2,txt);//存入文件名及结尾的0
	char *p3=p2+strlen(p2)+1;
	strcpy(p3,"octet");//存入模式位及结尾的0
	int packlen=4+strlen(p2)+strlen(p3);
	if(sendto(cfd,pack,packlen,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("upload request sendto error");
		return -1;
	}
	//读取服务器的回复消息
	int ack=recv_ack(cfd,sin);
	if(ack==-1)
	{
		puts("recv_ack error");
		return -1;
	}
	//2.开始上传数据
	//2.1只读形式打开要上传的文件
	int rfd=open(txt,O_RDONLY);
	if(rfd==-1)
	{
		perror("upload open error");
		return -1;
	}
	int i=1;
	while(1)
	{
		bzero(pack,sizeof(pack));
		//2.2设置发送数据的协议包
		*p1=htons(3);//前两字节操作码为3时代表此为数据包
		*(p1+1)=htons(i);//设置块编码从1开始
		int res=read(rfd,pack+4,512);//从文件中读取512数据存入数据域
		packlen=4+res;//本次的数据包大小
		//发送数据
		if(sendto(cfd,pack,sizeof(pack),0,(struct sockaddr*)&sin,sizeof(sin))==-1)
		{
			perror("upload data sandto error");
			return -1;
		}
		ack=recv_ack(cfd,sin);//读取服务器的回复消息
		if(ack==-1)
		{
			puts("recv_ack error");
			return -1;
		}else if(ack<i)//服务器返回的块编码小于当前发送的
		{
			//光标返回发送之前的位置重新发送
			lseek(rfd,-res,SEEK_CUR);
		}else if(ack==i)//服务器已接收该数据包可以发送下一个
		{
			i++;//块编码+1
		}
		if(res<512)//发送完成
		{break;}
	}
	close(rfd);
	return 0;
}
int my_save_data(char *p,int len)
{
	//追加写的形式创建一个文件存储下载的数据
	int wfd=open("./downtxt",O_WRONLY|O_APPEND|O_CREAT,0664);
	if(wfd==-1)
	{
		perror("my_download open error");
		return -1;
	}
	write(wfd,p,len);
	close(wfd);
	return 0;
}
int recv_ack(int cfd,struct sockaddr_in sin)
{
	char pack[516]="";
	//接收服务器发来的消息 存入pack
	int sinlen=sizeof(sin);
	int res=-1;
	if((res=(recvfrom(cfd,pack,sizeof(pack),0,(struct sockaddr*)&sin,&sinlen)))==-1)
	{
		perror("recv_ack recvfrom error");
		return -1;
	}
	short *p=(short*)pack;
	short num=ntohs(*p);//获取发来消息的操作码
	switch(num)
	{
		case 3:
			{
				printf("收到数据包,块编码:%d\n",ntohs(*(p+1)));
				//保存数据
				int seve=my_save_data(pack+4,res-4);
				if(seve==-1)
				{
					puts("my_save_data error");
					return -1;
				}
				//如果读取的长度小于516说明已经下载完成
				if(res<sizeof(pack))
				{
					//最后一次回复ACK
					*p=htons(4);
					sendto(cfd,pack,4,0,(struct sockaddr*)&sin,sizeof(sin));
					return 0;//正常下载完成返回0
				}
			}break;
		case 4:
			{
				printf("收到ACK,快编码:%d\n",ntohs(*(p+1)));
			}break;
		case 5:
			{
				//收到错误码 输出差错码和差错信息
				printf("ERROR:%d:%s\n",ntohs(*(p+1)),pack+4);
				return -1;
			}break;
	}
	return ntohs(*(p+1));//返回块编码
}

 


http://www.niftyadmin.cn/n/5403012.html

相关文章

深入理解Docker

文章目录 1 Docker理论1.1 背景知识1.2 是什么1.3 Docker基本三要素1.4 镜像原理1.5 安装教程 2 Docker常用命令2.0 防火墙相关命令2.1 镜像命令2.2 容器命令2.3 进阶命令 3. 实战之Docker部署springboot项目步骤一&#xff1a;Springboot项目配置1.1 添加docker的maven依赖1.2…

mysql 常用命令练习

管理表格从表中查询数据从多个表查询修改数据sql变量类型 管理表格 创建一个包含三列的新表 CREATE TABLE products (id INT,name VARCHAR(255) NOT NULL,price INT DEFAULT 0,PRIMARY KEY(id) // 自增 ); 从数据库中删除表 DROP TABLE product; 向表中添加新列 ALTER TAB…

在Linux系统安装python3.7以及pip3

最近在编译mediasoup-demo-v3的时候&#xff0c;要用到python3.7以上的版本&#xff0c;但是本地yum用到了phthon2.7&#xff0c;强行升级会导致yum的不可用&#xff0c;本文记录在不影响python2的情况下&#xff0c;升级使用python3的方法。 我这里安装的是3.7.4版本&#xf…

AI大预言模型——ChatGPT与AI绘图及论文高效写作

原文链接&#xff1a;AI大预言模型——ChatGPT与AI绘图及论文高效写作 2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网…

【动态规划】动态规划算法基本概念,原理应用和示例代码

1 动态规划概述 动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种解决多阶段决策问题的数学优化方法。它将原问题分解成若干个子问题&#xff0c;通过解决子问题只需解决一次并将结果保存下来&#xff0c;从而避免了重复计算&#xff0c;提高了…

高级统计方法 第5次作业

作业评阅&#xff1a; 概念 1.问题 2.问题&#xff08;略&#xff09; 4.问题&#xff08;略&#xff09; &#xff08;a&#xff09;问题&#xff08;略&#xff09; 10%&#xff0c;忽略 X < 0.05和 X > 0.95的情况。 &#xff08;b&#xff09;问题&#xff08;略…

EasyRecovery2024国产免费的手机数据恢复软件

一、功能介绍 EasyRecovery手机数据恢复软件是一款功能全面的数据恢复工具&#xff0c;专为移动设备设计。其主要功能包括&#xff1a; 文件恢复&#xff1a;能够恢复手机中因各种原因丢失的文件&#xff0c;如照片、视频、音频、文档等。深度扫描&#xff1a;通过深度扫描手…

数据中心GPU集群高性能组网技术分析

数据中心GPU集群组网技术是指将多个GPU设备连接在一起&#xff0c;形成一个高性能计算的集群系统。通过集群组网技术&#xff0c;可以实现多个GPU设备之间的协同计算&#xff0c;提供更大规模的计算能力&#xff0c;适用于需要大规模并行计算的应用场景。 常用的组网技术&…