2024.8.19 学习记录 —— 作业

一、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;
}
相关推荐
岁岁岁平安2 分钟前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA5 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
qq_5895681010 分钟前
数据可视化echarts学习笔记
学习·信息可视化·echarts
sdaxue.com18 分钟前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
m0_7482475530 分钟前
github webhooks 实现网站自动更新
github
兔C1 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序
海海不掉头发1 小时前
苍穹外卖-day05redis 缓存的学习
学习·缓存
张国荣家的弟弟2 小时前
【Yonghong 企业日常问题04】永洪BI可视化工具Linux部署全攻略(部署详解版)
linux·运维·github
小木_.2 小时前
【Python 图片下载器】一款专门为爬虫制作的图片下载器,多线程下载,速度快,支持续传/图片缩放/图片压缩/图片转换
爬虫·python·学习·分享·批量下载·图片下载器
一棵开花的树,枝芽无限靠近你2 小时前
【PPTist】组件结构设计、主题切换
前端·笔记·学习·编辑器