UDP聊天室项目

news/2024/9/18 23:27:38 标签: udp, linux

代码思路

服务器

#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.niftyadmin.cn/n/5664695.html

相关文章

实时(按帧)处理的低通滤波C语言实现

写在前面&#xff1a; 低通滤波采用一般的FIR滤波器&#xff0c;因为本次任务&#xff0c;允许的延迟较多&#xff0c;或者说前面损失的信号可以较多&#xff0c;因此&#xff0c;涉及一个很高阶的FIR滤波器&#xff0c;信号起始段的信号点可以不处理&#xff0c;以及&#xf…

p14 使用阿里云服务器的docker部署NGINX

拉取NGINX的镜像 这里因为之前已经配置过从阿里云的镜像仓库里面拿镜像所以这里直接就执行docker pull nginx拉取NGINX镜像就OK了 运行NGINX镜像 这里执行docker run -d --name nginx01 -p 3344:80 nginx这里3344是服务器访问的端口80是容器内部的端口&#xff0c;可以看到…

C语言操作数据库

目录 一、引言 二、环境准备 三、C语言操作数据库步骤 1.数据库连接 2.数据库查询 3.数据库插入、更新和删除 四、总结 本文将详细介绍如何在C语言中操作数据库&#xff0c;包括数据库的连接、查询、插入、更新和删除等操作。通过本文的学习&#xff0c;读者可以掌握C语言操…

ruby和python哪个好学

Ruby和python都挺好学的。建议学习Python&#xff0c;语法的话&#xff0c;Python相对更简洁。而且Python应用场合更广泛&#xff0c;运维、网站开发、数据处理、科学研究都可以。 Ruby和Python十分相似&#xff0c;有很多共同点&#xff0c;但也有一些不同之外&#xff0c;以…

二叉树OJ题——对称二叉树

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 对称二叉树 二、解题思路 三、解题代码

代码随想录算法训练营第三十六天 | 1049. 最后一块石头的重量 II,494. 目标和,474.一和零

第三十六天打卡&#xff0c;今天的题还是比较抽象&#xff0c;特别是后面两题&#xff0c;他们是01背包问题&#xff0c;只是递推公式变了&#xff0c;这点不容易想到 1049.最后一块石头的重量Ⅱ 题目链接 解题过程 dp[j]表示容量&#xff08;这里说容量更形象&#xff0c;其…

MySQL连接相关知识点

MySQL连接相关知识点 1. 查看当前连接 看当前 MySQL 的连接数和相关信息&#xff1a; SHOW STATUS LIKE Threads_connected;查看详细的连接信息&#xff0c;包括每个会话的状态&#xff0c;可以使用以下命令&#xff1a; SHOW PROCESSLIST;查看所有连接&#xff0c;包括没有…

java重点学习-JVM调优实践

12.15 JVM 调优的参数可以在哪里设置参数值 war包部署在tomcat中设置 修改TOMCAT HOME/bin/catalina.sh文件jar包部署在启动参数设置 java -Xms512m -Xmx1024m -jar xxxx,jar 12.16 JVM 调优的参数有哪些 对于IM调优&#xff0c;主要就是调整年轻代、老年代、元空间的内存空间大…