一、TCP机械臂测试
#include <myhead.h>
#define SER_PORT 8888 // 与服务器保持一致
#define SER_IP "192.168.0.114" // 服务器ip地址
int main(int argc, const char *argv[])
{
// 创建文件描述符打开键盘文件
int fd = open("/dev/input/event1", O_RDONLY);
if (fd == -1)
{
perror("open error");
return -1;
}
// 定义结构体接收键盘文件的数据
struct input_event ie;
// 创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd);
// 连接到服务器
// 填充服务器地址信息结构体
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地址
// 连接服务器
if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("connect error");
return -1;
}
printf("连接服务器成功\n");
// 准备数据
char rbuf[5] = {0xff, 0x02, 0x00, 0x10, 0xff};
unsigned char bbuf[5] = {0xff, 0x02, 0x01, 0x10, 0xff};
// 发送给服务器,以初始化机械臂
send(cfd, rbuf, sizeof(rbuf), 0);
sleep(1);
send(cfd, bbuf, sizeof(bbuf), 0);
printf("%#x\n", rbuf[3]);
printf("%#x\n", bbuf[3]);
while (1)
{
// 接收键盘文件数据
read(fd, &ie, sizeof(ie));
switch (ie.code * ie.value)
{
case 32: // d键
rbuf[3] += 1;
if (rbuf[3] > 90)
{
rbuf[3] = 90;
}
send(cfd, rbuf, sizeof(rbuf), 0); // 传输数据给机械臂
break;
case 30: // a键
rbuf[3] -= 1;
if (rbuf[3] < -90)
{
rbuf[3] = -90;
}
send(cfd, rbuf, sizeof(rbuf), 0); // 传输数据给机械臂
break;
case 31: // s键
bbuf[3] -= 1;
if (bbuf[3] <= 0)
{
bbuf[3] = 0;
}
send(cfd, bbuf, sizeof(bbuf), 0); // 传输数据给机械臂
break;
case 17: // w键
bbuf[3] += 1;
if (bbuf[3] > 180)
{
bbuf[3] = 180;
}
if (bbuf[3] < 0)
{
bbuf[3] = 0;
}
send(cfd, bbuf, sizeof(bbuf), 0); // 传输数据给机械臂
break;
default:
break;
}
/*
if (buf == 'd') // 红机械臂右移一度
{
rbuf[3] = rbuf[3] + 1; // 修改角度
printf("%#x\n", rbuf[3]);
send(cfd, rbuf, sizeof(rbuf), 0); // 传输结果
}
if (buf == 'a') // 红机械臂左移一度
{
rbuf[3] = rbuf[3] - 1;
printf("%#x\n", rbuf[3]);
send(cfd, rbuf, sizeof(rbuf), 0);
}
if (buf == 's') // 蓝机械臂上移一度
{
bbuf[3] = bbuf[3] + 1;
printf("%#x\n", bbuf[3]);
send(cfd, bbuf, sizeof(bbuf), 0);
}
if (buf == 'w') // 蓝机械臂下移一度
{
bbuf[3] = bbuf[3] - 1;
printf("%#x\n", bbuf[3]);
send(cfd, bbuf, sizeof(bbuf), 0);
}
if (buf == 'q') // 直接设置
{
int val = 0;
printf("红机械臂增减角度:");
scanf("%d", &val); // 获取增减角度
rbuf[3] = rbuf[3] + val; // 修改角度
printf("%#x\n", rbuf[3]); // 传输结果
getchar();
printf("蓝机械臂增减角度:");
scanf("%d", &val);
bbuf[3] = bbuf[3] + val;
printf("%#x\n", bbuf[3]);
getchar();
send(cfd, rbuf, sizeof(rbuf), 0);
usleep(100);
send(cfd, bbuf, sizeof(bbuf), 0);
}
buf = 0;
*/
}
// 5、关闭套接字
close(cfd);
return 0;
}
二、基于UDP的TFTP文件传输
#include <myhead.h>
int main(int argc, char const *argv[])
{
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return -1;
}
// 下载请求包
char buf[516] = "";
short *p1 = (short *)buf; // 操作码
*p1 = htons(1);
char *p2 = buf + 2; // 文件名
strcpy(p2, "5.png");
char *p4 = p2 + strlen(p2) + 1; // 模式位
strcpy(p4, "octet");
int size = 2 + strlen(p2) + strlen(p4) + 2; // 请求包总长
// 服务器地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr("192.168.0.114");
// 向服务器发送a下载请求
sendto(sfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin));
int newfd = 0;
if ((newfd = open("./5.png", O_WRONLY | O_CREAT | O_APPEND | O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}
while (1)
{
socklen_t size_sin = sizeof(sin);
bzero(buf, sizeof(buf)); // 清空buf
int res = 0; // 接收服务器发送的数据
if ((res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &size_sin)) == -1)
{
perror("recvform error");
return -1;
}
if (res <516)
{
write(newfd, buf + 4, res - 4);
*p1 = htons(4);
sendto(sfd, buf, 4, 0, (struct sockaddr *)&sin, sizeof(sin));
printf("下载结束\n");
break;
}
write(newfd, buf + 4, res - 4); // 写入数据
*p1 = htons(4); // 操作码置位 4
sendto(sfd, buf, 4, 0, (struct sockaddr *)&sin, sizeof(sin)); // 发送ACK
}
close(sfd);
close(newfd);
return 0;
}
三、基于UDP的聊天室
1、服务器端
#include <myhead.h>
#define SER_PORT 6666 // 服务器端口号
#define SER_IP "192.168.179.128" // 服务器ip地址
// 定义结构体用于存储客服端的信息
struct MSG
{
char type; // 信息类型 'L'表示登录 'T'表示群聊 'Q'表示退出 'S'表示系统消息
char name[20];
char msg[256];
};
// 定义链表用于存储不同的客服端信息
struct ADDR
{
struct sockaddr_in cin;
struct ADDR *next;
};
// 登录操作的函数
void do_login(int sfd, struct MSG buf, struct ADDR *addr, struct sockaddr_in cin)
{
// 先遍历链表 将新用户加入群聊的消息发给所有人
struct ADDR *tmp = addr; // 记录链表头节点
while (tmp->next != NULL)
{
tmp = tmp->next;
if (-1 == sendto(sfd, &buf, sizeof(buf), 0, (struct sockaddr *)&(tmp->cin), sizeof(tmp->cin)))
{
perror("sendto error");
}
}
// 将新用户的网络信息结构体 头插入链表
struct ADDR *pnew = NULL;
if (NULL == (pnew = (struct ADDR *)malloc(sizeof(struct ADDR))))
{
printf("malloc error\n");
return;
}
pnew->cin = cin;
pnew->next = addr->next;
addr->next = pnew;
return;
}
// 群聊操作的函数
void do_chat(int sfd, struct MSG buf, struct ADDR *addr, struct sockaddr_in cin)
{
// 遍历链表,将群聊消息发给除了自己之外的所有人
struct ADDR *ptmp = addr;
while (ptmp->next != NULL)
{
ptmp = ptmp->next;
if (memcmp(&cin, &(ptmp->cin), sizeof(cin)))
{
// 不是自己 就发送数据
sendto(sfd, &buf, sizeof(buf), 0, (struct sockaddr *)&(ptmp->cin), sizeof(ptmp->cin));
}
}
return;
}
// 退出操作的函数
void do_quit(int sfd, struct MSG buf, struct ADDR *addr, struct sockaddr_in cin)
{
// 遍历链表 是自己就将自己在链表中删除
// 不是自己 就发送 退出群聊的数据
struct ADDR *ptmp = addr;
struct ADDR *del = NULL;
while (ptmp->next != NULL)
{
if (memcmp(&(ptmp->next->cin), &cin, sizeof(cin)))
{
// 不是自己
ptmp = ptmp->next;
strcat(buf.name,"-系统接管");
sendto(sfd, &buf, sizeof(buf), 0, (struct sockaddr *)&(ptmp->cin), sizeof(ptmp->cin));
}
else
{
// 是自己
del = ptmp->next;
ptmp->next = del->next;
free(del);
del = NULL;
}
}
return;
}
int main(int argc, char const *argv[])
{
// 1、创建用于通信的套接字文件描述符
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("scoket error");
return -1;
}
printf("sfd = %d\n", sfd); // 3
// 2、绑定ip地址和端口号
// 2.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地址
// 2.2、 绑定
if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
// 3、数据接收
struct MSG buf; // 定义用于信息接收的变量
// 定义用于接收客服端网络信息的变量
struct sockaddr_in cin; // 接收对端地址信息
socklen_t addrlen = sizeof(cin); // 接收地址长度
// 4、创建父子进程用于不同功能
pid_t pid = 0;
if (-1 == (pid = fork()))
{
perror("fork error");
return -1;
}
if (0 == pid) // 子进程,用于信息的处理
{
// 创建保存客户端信息的链表头节点
struct ADDR *addr;
if (NULL == (addr = (struct ADDR *)malloc(sizeof(struct ADDR))))
{
printf("malloc error\n");
return -1;
}
memset(addr, 0, sizeof(addr));
addr->next = NULL;
while (1)
{ // 循环收发数据
// 清空容器,确保信息准确
memset(&buf, 0, sizeof(buf));
memset(&cin, 0, sizeof(cin));
// 接收客户端发送的消息,存放在msg中
if (-1 == recvfrom(sfd, &buf, sizeof(buf), 0, (struct sockaddr *)&cin, &addrlen))
{
perror("recvfrom error");
return -1;
}
switch (buf.type)
{ // 判断消息中的操作码,根据操作码执行对应操作
case 'L': // 登录操作
do_login(sfd, buf, addr, cin);
break;
case 'T': // 群聊操作
do_chat(sfd, buf, addr, cin);
break;
case 'Q': // 退出操作
do_quit(sfd, buf, addr, cin);
break;
}
}
}
else // 父进程,用于发送系统信息
{
strcpy(buf.name, "系统消息");
buf.type = 'T';
while (1) // 循环在终端接收消息
{
memset(buf.msg, 0, sizeof(buf.msg));
printf("输入信息:");
fgets(buf.msg, 256, stdin);
buf.msg[strlen(buf.msg) - 1] = 0;
sendto(sfd, &buf, sizeof(buf), 0, (struct sockaddr *)&sin, addrlen);// 向子进程发送信息
}
}
// 4、关闭文件描述符
close(sfd);
return 0;
}
2、客服端
#include <myhead.h>
#define SER_PORT 6666 // 与服务器保持一致
#define SER_IP "192.168.179.128" // 服务器ip地址
// 用于信息发送的结构体
struct msgTyp
{
char type; // 信息类型 L-》注册名 T-》聊天信息 Q-》退出
char name[20]; // 注册名
char msg[256]; // 聊天信息
};
int main(int argc, char const *argv[])
{
// 1、 创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd);
// 2、信息发送
// 2.1数据结构体定义
struct msgTyp buf;
bzero(&buf, 0);
// 2.2 填充服务器地址信息结构体
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地址
socklen_t seraddr_len = sizeof(sin); // 服务器信息结构体大小
// 2.3 注册
buf.type = 'L'; // 指定消息类型
printf("注册用户名:");
fgets(buf.name, sizeof(buf.name), stdin);
buf.name[strlen(buf.name) - 1] = 0;
strcpy(buf.msg, "登录");
// 向服务器发送登录信息
sendto(cfd, &buf, sizeof(buf), 0, (struct sockaddr *)&sin, seraddr_len);
// 3 创建父子进程用于处理不同工作
pid_t pid = 0;
if (-1 == (pid = fork()))
{
perror("fork error");
return -1;
}
// 子进程,用于接收信息
if (0 == pid)
{
while (1)
{
if (-1 == recvfrom(cfd, &buf, sizeof(buf), 0, NULL, NULL))
{
perror("recvfrom error");
return -1;
}
// 打印接收到的信息
printf("[%s]:%s\n", buf.name, buf.msg);
}
}
else if (0 < pid) // 父进程,用于向其他客服端发送信息
{
while (1)
{
bzero(&buf, 0); // 清空容器,以保证信息的准确性
printf("输入信息:");
fgets(buf.msg, 256, stdin);
buf.msg[strlen(buf.msg) - 1] = 0;
if (!strcmp(buf.msg, "quit"))
{
buf.type = 'Q';
strcpy(buf.msg, "退出群聊");
sendto(cfd, &buf, sizeof(buf), 0, (struct sockaddr *)&sin, seraddr_len);
break;
}
else
{
buf.type = 'T';
// 发送信息
sendto(cfd, &buf, sizeof(buf), 0, (struct sockaddr *)&sin, seraddr_len);
}
}
// 退出子进程
kill(pid, SIGKILL);
wait(NULL); // 阻塞回收子进程资源
}
// 3、关闭套接字
close(cfd);
return 0;
}