当前位置: 首页 > article >正文

UDP聊天室项目

代码思路

服务器

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

//链表节点结构体:
typedef struct node
{
    struct sockaddr_in addr; //data   memcmp
    struct node *next;
} link_t;

//类型
enum type_t
{
    login,
    chat,
    quit,
};

//消息对应的结构体(同一个协议)
typedef struct msg_t
{
    int type;       //'L' C  Q    enum un{login,chat,quit};
    char name[32];  //用户名
    char text[128]; //消息正文
} MSG_t;

link_t *link_creat();
void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);
void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr);

int main(int argc, char const *argv[])
{
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    // 绑定
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = INADDR_ANY;
    socklen_t len = sizeof(caddr);
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind ok\n");
    // 创建链表
    link_t *p = link_creat();
    printf("creat ok\n");
    MSG_t msg;
    // 可扩展功能,服务器可以给所有用户发送“公告”
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        while (1)
        {

            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            msg.type = chat;
            strcpy(msg.name, "server");
            sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
        }
    }
    else
    {
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
            {
                perror("recvfrom err");
                return -1;
            }
            printf("type:%d\n", msg.type);
            switch (msg.type)
            {
            case login: // 上线函数
                client_login(sockfd, p, msg, caddr);
                break;
            case chat: // 聊天函数
                client_chat(sockfd, p, msg, caddr);
                break;
            case quit: // 下线函数
                client_quit(sockfd, p, msg, caddr);
                break;
            default:
                break;
            }
        }
    }

    close(sockfd);

    return 0;
}
// 创建链表
link_t *link_creat()
{
    // 给头结点开辟空间
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if (p == NULL)
    {
        perror("malloc err");
        return NULL;
    }
    // 初始化头结点
    p->next = NULL;
    return p;
}

// 客户端上线功能:将用户上线消息发送给其他用户,并将用户信息添加到链表中
void client_login(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    printf("%s  login....\n", msg.name);
    // 先遍历链表
    // 再插入新的节点
    while (p->next != NULL)
    {
        p = p->next;
        sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
    link_t *pnew = (link_t *)malloc(sizeof(link_t));
    pnew->addr = caddr;
    pnew->next = NULL;
    p->next = pnew;

    return;
}
// 聊天功能:转发消息到在链表中除了自己的所有用户
void client_chat(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    printf("%s says %s\n", msg.name, msg.text);
    while (p->next != NULL)
    {
        p = p->next;
        // strcmp:对比前后两个字符串中的内容是否一致
        // memcmp:对比前后两个对应的地址(任意类型)的内容是否一致
        if (memcmp(&(p->addr), &caddr, sizeof(caddr)))
        {
            sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
        }
    }

    return;
}
// 客户端退出功能:将用户下线消息发送给其他用户,将下线用户从链表中删除
void client_quit(int sockfd, link_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    printf("%s quit.......\n", msg.name);
    link_t *pdel = NULL;
    while (p->next != NULL)
    {
        if (memcmp(&(p->next->addr), &caddr, sizeof(caddr)))
        {
            // 不是自己
            p = p->next;
            sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
        }
        else
        {
            // 是自己,删除节点
            pdel = p->next;
            p->next = pdel->next;
            free(pdel);
            pdel = NULL;
        }
    }
    return;
}

客户端

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>

//链表节点结构体:
typedef struct node
{
    struct sockaddr_in addr; //data   memcmp
    struct node *next;
} link_t;

//类型
enum type_t
{
    login,
    chat,
    quit,
};

//消息对应的结构体(同一个协议)
typedef struct msg_t
{
    int type;       //'L' C  Q    enum un{login,chat,quit};
    char name[32];  //用户名
    char text[128]; //消息正文
} MSG_t;

int main(int argc, char const *argv[])
{
    int ret;
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd);
    // 绑定
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t len = sizeof(caddr);

    MSG_t msg;
    // 给服务器发送上线消息
    printf("请输入用户名:");
    fgets(msg.name, sizeof(msg.name), stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';
    msg.type = login;
    sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0) // 循环接受服务器消息
    {
        while (1)
        {
            ret = recvfrom(sockfd, &msg, sizeof(MSG_t), 0, NULL, NULL);
            if (ret < 0)
            {
                perror("recv from err");
                return -1;
            }
            if (msg.type == login)
                printf("%s login.......\n", msg.name);
            else if (msg.type == chat)
                printf("%s says %s\n", msg.name, msg.text);
            else if (msg.type == quit)
                printf("%s quit.......\n", msg.name);
        }
    }
    else // 循环发送消息
    {
        while (1)
        {
            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            if (!strcmp(msg.text, "quit"))
            {
                msg.type = quit;
                sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
                break;
            }
            else
            {
                msg.type = chat;
                sendto(sockfd, &msg, sizeof(MSG_t), 0, (struct sockaddr *)&saddr, sizeof(saddr));
            }
        }
        kill(pid, SIGINT);
        wait(NULL);
    }
    close(sockfd);
    return 0;
}

http://www.kler.cn/news/303896.html

相关文章:

  • 若依系统(Security)增加微信小程序登录(自定义登录)
  • nginx部署时的路径配置问题
  • 网络安全要点总结
  • 第四届长城杯-misc
  • 如何使用命令安装android的.aab包
  • Cesium 问题:视角漫游时添加的无人机模型飞行时有抖动
  • 隧道代理的原理及其挑选指南
  • 828华为云征文 | Flexus X实例在华为云EulerOS环境中部署堡垒机Jumpserver的详细指南
  • JS获取页面中video标签视频的封面和时长
  • 代理IP池纯净度对数据抓取有影响吗?
  • ​2024年最新python教程全套,现在分享给大家(python全栈)
  • 2024年最新软件测试面试题【附文档答案】
  • websim.ai 体验过程+感受
  • 《创新电力巡检,机器人铸就安全高效未来》
  • Windows编译MongoDB的C++库,并使用Qt调用
  • centos8构建nginx1.27.1+BoringSSL+http3+lua+openresty
  • 如何在 Vue 3 中使用 Element Plus
  • 3.3k star开源的Notepad++文本编辑器替代品,跨平台
  • 从底层原理上解释 clickhouse 保证完全的幂等性
  • 皮皮鲁周边商品网络销售系统的设计与实现---附源码74752
  • Android前台服务如何在后台启动activity?
  • Leetcode 每日一题:Word Ladder
  • Autosar模式管理实战系列-COMM模块状态机及重要函数讲解
  • neo4j docker 运行4.35 community 版本失败
  • 氢能源多旋翼无人机技术详解
  • vue3.0 使用echarts与echarts-gl 实现3D饼图
  • Spring Boot中实现跨域请求
  • 网约车APP开发指南:基于同城代驾系统源码的实现路径
  • STM32G474RE之RTC
  • C++——内存管理