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

news2024/9/24 7:21:14

一、编写基于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.coloradmin.cn/o/1483612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【办公类-21-05】20240227单个word按“段落数”拆分多个Word(成果汇编 只有段落文字 1拆5)

作品展示 背景需求 前文对一套带有段落文字和表格的word进行13份拆分 【办公类-21-04】20240227单个word按“段落数”拆分多个Word&#xff08;三级育婴师操作参考题目1拆13份&#xff09;-CSDN博客文章浏览阅读293次&#xff0c;点赞8次&#xff0c;收藏3次。【办公类-21-04…

多余图片人物有什么办法可以去掉?一分钟教你搞定

在我们的快节奏生活中&#xff0c;旅游已经成为了一种宝贵的放松方式&#xff0c;它让我们有机会暂时逃离日常的喧嚣&#xff0c;感受大自然的宁静与美丽。每一次踏足新的土地&#xff0c;我们都会被各种独特的风景所吸引&#xff0c;从雄伟的山川到细腻的街景&#xff0c;每一…

市场占比不足1.5%,折叠屏手机为何香不起来?

2007年&#xff0c;取消了实体按键键盘的iPhone诞生&#xff0c;不到四年的时间&#xff0c;就将诺基亚变成了历史。依靠率先发起的触屏技术&#xff0c;苹果快速抢占了全球智能手机市场&#xff0c;并掀起了智能机迭代的潮流。 但折叠屏手机显然没那么幸运。2018年&#xff0…

AcWing 788. 逆序对的数量 解题思路及代码

先贴个题目&#xff1a; 以及原题链接&#xff1a; 788. 逆序对的数量 - AcWing题库https://www.acwing.com/problem/content/790/ 这题也是板子题&#xff0c;就是对归并排序的衍生&#xff0c;我们先分析下如果用归并排序对排序区间进行二分的话&#xff0c;逆序对可能出现的…

【DAY07 软考中级备考笔记】数据结构:线性结构,数组矩阵和广义表

数据结构&#xff1a;线性结构&#xff0c;数组矩阵和广义表 3月2日 – 天气&#xff1a;晴 1. 线性表的定义和存储方式 > 这一部分只需要掌握下面的两点即可&#xff1a; > > * 采用顺序存储和链式存储的特点 > * 单链表的插入和删除操作 2. 栈和队列 > 这里需…

antvX6 - Vue自定义节点,并实现多种画布操作,拖拽、缩放、连线、双击、检索等等

一、 首先 antv x6 分为两个版本 低版本和高版本 我这里是使用的2.0版本 并且搭配了相关插件 例如&#xff1a;画布的图形变换、地图等 个人推荐 2.0版本&#xff0c;高版本配置多&#xff0c;可使用相关插件多&#xff0c;但是文档描述小&#xff0c;仍在更新&#xff0c; 低…

【书生·浦语大模型实战营】第三节 课后作业

基于 InternLM 和 LangChain 搭建你的知识库 0.课程链接1.课后作业1.1基础作业&#xff1a;1.2 进阶作业&#xff1a; 0.课程链接 课程标题&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库 课程链接&#xff1a;https://github.com/InternLM/tutorial/blob/main/langch…

【体育】体育锻炼之体能耐力、户外运动、中华武术(附大学生体质健康标准)

程序员养生指南之 【体育】体育锻炼之体能耐力、户外运动、中华武术&#xff08;附大学生体质健康标准&#xff09; 文章目录 一、如何增强耐力与体能&#xff1f;1、耐力训练的原理2、如何进行耐力训练3、如何提高长跑成绩 二、户外运动之绳结与下降1、绳结2、下降 三、中华武…

C语言 for 循环语句的基本格式是什么?

一、问题 for 循环语句在C语⾔中是最为常见的循环语句&#xff0c;其功能强⼤&#xff0c;⽽且⽤法灵活&#xff0c;那么它的基本格式是什么呢&#xff1f; 二、解答 for 语句的⼀般形式为&#xff1a; for(表达式1;表达式2;表达3&#xff09;语句; 每条 for 语句包含三个⽤分…

2023国赛样题路由部分【RIP RIPNG ACLRIP ACLRIPNG ISIS NAT64】

RT1串行链路、RT2串行链路、FW1、AC1之间分别运行RIP和RIPng协议&#xff0c;FW1、RT1、RT2的RIP和RIPng发布loopback2地址路由&#xff0c;AC1 RIP发布loopback2地址路由&#xff0c;AC1 RIPng采用route-map匹配prefix-list重发布loopback2地址路由。RT1配置offset值为3的路由…

ES6 | (二)ES6 新特性(下) | 尚硅谷Web前端ES6教程

文章目录 &#x1f4da;迭代器&#x1f407;定义&#x1f407;工作原理&#x1f407;自定义遍历数据 &#x1f4da;生成器函数&#x1f407;声明和调用&#x1f407;生成器函数的参数传递&#x1f407;生成器函数案例 &#x1f4da;Promise&#x1f4da;Set&#x1f407;Set的定…

输入一个整数,输出其最长连续因子。

输入一个整数&#xff0c;输出其最长连续因子。 例如 输入&#xff1a;60 输出&#xff1a;2 3 4 5 6 注意&#xff1a;1不算因子 输入输出格式 输入描述: 输入一个整数N&#xff0c;N<10000。 输出描述: 输出其最长连续因子&#xff0c;如果有多个最长&#xff0c;输出…

vcruntime140.dll为什么都是?解决vcruntime140.dll丢失的七种办法

计算机中一个常见的错误提示是“无法继续执行代码&#xff0c;因为找不到vcruntime140.dll”。这个问题的出现可能是由于vcruntime140.dll文件丢失或损坏导致的。本文将介绍解决这个问题的7个方法&#xff0c;并详细解释vcruntime140.dll丢失的原因以及其属性介绍。 一、vcrun…

解决在 Mac 上安装 Adobe 软件弹出提示:安装包已经被损坏并且不能被打开。

问题&#xff1a; “INSTALLER” is damaged and can’t be opened. You should eject the disk image. 解决方法和步骤&#xff1a; 打开安装包&#xff1b;将安装包 “INSTALLER” 拖动复制到某个文件夹&#xff0c;复制后的文件路径例如像这样&#xff1a;/Users/michael…

Pake 轻松构建轻量级多端桌面应用

Pake 利用 Rust 轻松构建轻量级多端桌面应用&#xff0c;支持 Mac / Windows / Linux。 小白用户&#xff1a;可以使用 「常用包下载」 方式来体验 Pake 的能力&#xff0c;也可试试 Action 方式。 开发用户&#xff1a;可以使用 「命令行一键打包」&#xff0c;对 Mac 比较友…

二维码门楼牌管理系统技术服务:构建智慧城市新标准

文章目录 前言一、二维码门楼牌管理系统的诞生背景二、标准地址编码的定义与作用三、二维码门楼牌管理系统的核心技术四、二维码门楼牌管理系统的应用优势五、二维码门楼牌管理系统在智慧城市建设中的作用六、结论与展望 前言 随着城市化的快速发展&#xff0c;传统的门楼牌管…

波奇学Linux:进程通信之消息队列,操作系统管理ipc资源,信号量

进程通信的前提&#xff1a;必须让不同进程看到同一份资源。 管道&#xff1a;文件缓冲区 共享内存&#xff1a;内存块 消息队列&#xff1a;队列 消息队列 让不同的进程看到同一个队列&#xff0c;允许不同的进程向内核发送带类型的数据块 带类型是为了区分数据块是由哪个…

Linux——匿名管道

Linux——匿名管道 什么是管道匿名管道的底层原理观察匿名管道现象读写端的几种情况写端慢&#xff0c;读端快写端快&#xff0c;读端慢 管道的大小写端关闭&#xff0c;读端一直读写端一直写&#xff0c;读端关闭 我们之前一直用的是vim来编写代码&#xff0c;现在有了vscode这…

Vue路由(黑马程序员)

路由介绍 将资代码/vue-project(路由)/vue-project/src/views/tlias/DeptView.vue拷贝到我们当前EmpView.vue同级&#xff0c;其结构如下&#xff1a; 此时我们希望&#xff0c;实现点击侧边栏的部门管理&#xff0c;显示部门管理的信息&#xff0c;点击员工管理&#xff0c;显…

CK98-数学家键盘配置

官方驱动和说明书下载地址 https://www.coolkiller.cn/download/lists_6.html 介绍&#xff1a;https://new.qq.com/rain/a/20221229A09B1M00 官方CK-98数学家驱动版本&#xff08;谨慎更新&#xff09; 如果升级驱动出现问题&#xff0c;重启驱动软件后会默认让你恢复的。 …