1>操控机械臂:
通过w(红色臂角度增大)s(红色臂角度减小)
d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂
源代码:
#include <myhead.h>
#define minStep 10 //最小偏移角度
#define SER_IP "10.168.1.116" //服务器ip
#define SER_PORT 8888 //服务器端口号
#define CLI_IP "10.168.1.111 " //客户端IP
#define CLI_PORT 9999 //客户端端口号
char scanKeyboard(); //从键盘获取控制符
void r_angleUp(int fd, char buf[], int buf_size, char skb); //红色臂向右偏移
void r_angleDown(int fd, char buf[], int buf_size, char skb); //红色臂向左偏移
void b_angleUp(int fd, char buf[], int buf_size, char skb); //蓝色色臂向右偏移
void b_angleDown(int fd, char buf[], int buf_size, char skb); //蓝色臂向左偏移
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd); //3
//2、绑定(非必须)
//2.1 填充地址信息结构体
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(CLI_PORT);
cin.sin_addr.s_addr = inet_addr(CLI_IP);
//2.2 绑定
if (bind(cfd, (struct sockaddr *)&cin, sizeof(cin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//3、连接服务器
//3.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); //ip地址
//3.2 连接服务器
if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("connect error");
return -1;
}
printf("connect success\n");
//4、数据收发
char rbuf[5] = {0xff, 0x02, 0x00, 0x00, 0xff};
unsigned char bbuf[5] = {0xff, 0x02, 0x01, 0x00, 0xff};
//发送给服务器当做初始值
send(cfd, rbuf, sizeof(rbuf), 0);
sleep(1);
send(cfd, bbuf, sizeof(bbuf), 0);
printf("请键入W、S、A、D进行控制:");
while (1)
{
char skb = scanKeyboard();
if (skb == 27)
printf("\n请键入W、S、A、D进行控制(键入Q退出控制!):\n");
switch (skb)
{
case 'W':
case 'w':
{
int buf_size1 = sizeof(rbuf);
r_angleUp(cfd, rbuf, buf_size1, skb);
}
break;
case 'S':
case 's':
{
int buf_size1 = sizeof(rbuf);
r_angleDown(cfd, rbuf, buf_size1, skb);
}
break;
case 'D':
case 'd':
{
int buf_size2 = sizeof(bbuf);
b_angleUp(cfd, bbuf, buf_size2, skb);
}
break;
case 'A':
case 'a':
{
int buf_size2 = sizeof(rbuf);
b_angleDown(cfd, bbuf, buf_size2, skb);
}
break;
case 'Q':
{
printf("\n控制已结束\n");
return -1;
}
break;
}
}
//5、关闭套接字
close(cfd);
return 0;
}
char scanKeyboard()
{
//终端有三种工作模式:规范模式、非规范模式、原始模式
//非规范模式:所有输入时即时有效的,不需要用户另外输入行结束符
char in;
//定义两个termios结构体变量
struct termios new_settings; //保存用于操作的终端属性
struct termios stored_settings; //保存当前的终端属性,以便之后恢复
//把标准输入的状态存储在终端配置参数stored_settings中
tcgetattr(0, &stored_settings);
//把当前终端属性的副本赋值给new_settings,准备对其进行修改。
new_settings = stored_settings;
//修改new_settings的c_lflag字段,关闭规范模式(ICANON)。
//在非规范模式下,输入是即时的,不需要按回车键。
new_settings.c_lflag &= (~ICANON);
//设置new_settings的VTIME为0。
//非规范模式下读取输入时不使用超时。
new_settings.c_cc[VTIME] = 0; //非规范模式读取时的超时时间--VTIME
//tcgetattr(0, &stored_settings);
//在非规范模式下读取输入时至少需要读取1个字符才能返回
new_settings.c_cc[VMIN] = 1; //非规范模式读取时的最小字符数--VMIN
//将标准输入的属性设置为new_settings,TCSANOW参数指示更改应立即生效,不等待数据传输完成。
tcsetattr(0, TCSANOW, &new_settings); //不等数据传输完毕就立即改变属性--TCSANOW
in = getchar();
//读取完字符后,恢复标准输入的原始属性
tcsetattr(0, TCSANOW, &stored_settings);
return in;
}
void r_angleUp(int fd, char buf[], int buf_size, char skb)
{
if (skb != 27) //skb==ECS时退出控制
{
buf[3] += minStep; //每次操作的角度偏移minStep度
if (buf[3] >= 90)
{
buf[3] = 90;
}
send(fd, buf, buf_size, 0);
}
return;
}
void r_angleDown(int fd, char buf[], int buf_size, char skb)
{
if (skb != 27)
{
buf[3] -= minStep;
if (buf[3] <= -90)
{
buf[3] = -90;
}
send(fd, buf, buf_size, 0);
}
return;
}
void b_angleUp(int fd, char buf[], int buf_size, char skb)
{
if (skb != 27)
{
buf[3] += minStep;
if (buf[3] >= 180)
{
buf[3] = 180;
}
send(fd, buf, buf_size, 0);
}
return;
}
void b_angleDown(int fd, char buf[], int buf_size, char skb)
{
if (skb != 27)
{
buf[3] -= minStep;
if (buf[3] <= 0)
{
buf[3] = 0;
}
send(fd, buf, buf_size, 0);
}
return;
}
效果展示:
机械臂演示
2>基于UDP的TFTP文件传输
源代码:
#include <myhead.h>
#define SER_IP "10.168.1.116"
#define SER_PORT 69
#define MAXSIZE 1024
#define FILESIZE 128
enum mode_opt
{
READ = 1,
WRITE = 2,
DATA = 3,
ACK = 4,
ERR = 5
};
void request_WR(char pack[], int pack_size, char *name, int mode, int *packlen)
{
bzero(pack, pack_size);
char *p = pack;
short int *p1 = (short int *)p;
*p1 = htons(mode); //设置操作码
char *p2 = (char *)(p1 + 1); //**********
strcpy(p2, name); //文件名
char *p4 = p2 + strlen(p2) + 1;
strcpy(p4, "octet"); //模式位
*packlen = 4 + strlen(p2) + strlen(p4); //请求包的大小
}
int download(int cfd, struct sockaddr_in sin, char *name)
{
char pack[MAXSIZE] = "";
int packlen = 0;
//组件协议包:下载请求
request_WR(pack, sizeof(pack), name, READ, &packlen);
sendto(cfd, pack, packlen, 0, (struct sockaddr *)&sin, sizeof(sin));
int rcvlen = 0;
int fd = -1;
if ((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
{
perror("open error");
close(fd);
return -1;
}
//添加了对块编号的检查和管理
unsigned short expected_block_num = 1;
char data[MAXSIZE] = "";
socklen_t socklen = sizeof(sin);
while (1)
{
//收取服务器发来的数据包
rcvlen = recvfrom(cfd, data, sizeof(data), 0, (struct sockaddr *)&sin, &socklen);
if (rcvlen == -1)
{
perror("recv error");
return -1;
}
short int *p1_data = (short int *)data;
short code = ntohs(*p1_data);
short block_num = ntohs(*(p1_data + 1));
if (code == DATA)
{
if (block_num == expected_block_num) //只有当接收到的数据包的块编号符合预期时,才会写入文件并发送ACK
{
write(fd, data + 4, rcvlen - 4);
char ack[4];
*(short *)ack = htons(ACK);
*(short *)(ack + 2) = htons(block_num); // 确认收到的块编号
sendto(cfd, ack, 4, 0, (struct sockaddr *)&sin, sizeof(sin));
if (rcvlen < 516) // 如果接收到的数据包小于516字节,表示是最后一个数据包
{
printf("下载完成\n");
break;
}
expected_block_num++;
}
}
else if (code == ERR)
{
printf("ERROR:%d\n", block_num); // block_num在这里作为错误码
close(fd);
return -1; // 返回错误码
}
}
close(fd);
return 0;
}
int upload(int cfd, struct sockaddr_in sin, char *name)
{
char packet[MAXSIZE] = "";
int packlen = 0;
request_WR(packet, sizeof(packet), name, WRITE, &packlen);
sendto(cfd, packet, packlen, 0, (struct sockaddr *)&sin, sizeof(sin));
int fd = -1;
if ((fd = open(name, O_RDONLY)) == -1)
{
perror("open error");
return -1;
}
char buf[MAXSIZE] = "";
socklen_t socklen = sizeof(sin);
int rcvlen = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &socklen);
short int *p1_data = (short int *)buf;
提取ACK的操作码
short code = ntohs(*p1_data);
//提取ACK的块编号
unsigned short block_num = ntohs(*(p1_data + 1));
if (code != ACK || block_num != 0)
{
printf(" ACK or block error\n");
close(fd);
return -1;
}
unsigned short send_block_num = 1; // 设置发送数据包的块编号
while (1)
{
bzero(buf, sizeof(buf));
int num = read(fd, buf + 4, 512);
if (num < 0)
{
perror("read error");
close(fd);
return -1;
}
// 构造数据包头部(操作码和块编号)
*(short *)packet = htons(DATA); // 设置操作码为DATA
*(short *)(packet + 2) = htons(send_block_num); // 设置数据块编号
memcpy(packet + 4, buf + 4, num); // 复制文件数据到数据包
// 发送数据包
sendto(cfd, packet, num + 4, 0, (struct sockaddr *)&sin, socklen);
// 等待ACK
rcvlen = recvfrom(cfd, buf, 4, 0, (struct sockaddr *)&sin, &socklen);
if (rcvlen < 4)
{
perror("recv ACK error");
close(fd);
return -1;
}
p1_data = (short int *)buf;
code = ntohs(*p1_data); // 提取操作码
block_num = ntohs(*(p1_data + 1)); // 提取块编号
// 检查ACK的块编号是否与发送的数据包匹配
if (code != ACK || block_num != send_block_num)
{
printf("ACK or block error\n");
close(fd);
return -1;
}
if (num < 512) // 如果读取的数据小于512字节,表示文件已经发送完毕
{
printf("上传完毕\n");
break;
}
send_block_num++; // 准备下一个数据块的编号
}
close(fd);
return 0;
}
int main(int argc, char const *argv[])
{
printf("\t\t*************************\n");
printf("\t\t*********1.下载**********\n");
printf("\t\t*********2.上传**********\n");
printf("\t\t*********3.退出**********\n");
printf("\t\t*************************\n");
if (argc < 2)
{
printf("请输入IP\n");
return -1;
}
int choice;
scanf("%d", &choice);
getchar();
char name[FILESIZE];
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
int reuse = 1;
if (setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
socklen_t socklen = sizeof(sin);
switch (choice)
{
case 1:
{
printf("输入下载的文件:");
scanf("%s", name);
download(cfd, sin, name);
}
break;
case 2:
{
printf("输入上传的文件:");
scanf("%s", name);
upload(cfd, sin, name);
}
break;
case 3:
{
return 0;
}
break;
default:
{
printf("error choice\n");
}
break;
}
close(cfd);
return 0;
}
效果展示:
TFTP