实现网络聊天室(UDP)

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

服务器端:

#include<myhead.h>
struct sockaddr_in serveraddr, caddr;
enum type_t //枚举
{
    Login,
    Chat,
    Quit,
};
typedef struct MSG
{
    char type;    
    char name[32];  
    char text[128]; 
} msg_t;
 
typedef struct NODE //链表
{
    struct sockaddr_in caddr;
    struct NODE *next;
} node_t;
 
node_t *create_node(void) //建头节点
{
    node_t *p = (node_t *)malloc(sizeof(node_t));
    if (p == NULL)
    {
        perror("malloc err");
        return NULL;
    }
    p->next = NULL;
    return p;
}
void do_login(int, msg_t, node_t *, struct sockaddr_in); //登录的函数
void do_chat(int, msg_t, node_t *, struct sockaddr_in);  //群聊的函数
void do_quit(int, msg_t, node_t *, struct sockaddr_in);  //退出函数
int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("Usage:./a.out <port>\n");
        return -1;
    }
    //创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        exit(-1);
    }
    //填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t len = sizeof(caddr);
    //定义保存客户端网络信息的结构体
    //绑定套接字和服务器网络信息的结构体
    bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    printf("bind ok!\n");
    msg_t msg;
    node_t *p = create_node();
    while (1)
    {
        if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
        {
            perror("recvfrom err");
            return -1;
        }
        if (msg.type == Login)
        {
            strcpy(msg.text, "以上线");
            printf("ip:%s pord:%d name:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), msg.name);
            printf("状态:%s\n", msg.text);
            do_login(sockfd, msg, p, caddr);
        }
        else if (msg.type == Chat)
        {
            do_chat(sockfd, msg, p, caddr);
        }
        else if (msg.type == Quit)
        {
            strcpy(msg.text, "以下线");
            printf("ip:%s pord:%d name:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), msg.name);
            printf("状态:%s\n", msg.text);
            do_quit(sockfd, msg, p, caddr);
        }
    }
    close(sockfd);
    return 0;
}
//登录的函数
void do_login(int sockfd, msg_t msg, node_t *p, struct sockaddr_in caddr)
{
 
    sprintf(msg.text, "%s 以上线", msg.name);
    while (p->next != NULL)
    {
        p = p->next;
        sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
        //printf("%s\n",msg.text);
    }
    node_t *new = (node_t *)malloc(sizeof(node_t));
    //初始化
    new->caddr = caddr;
    new->next = NULL;
    //链接到链表尾
    p->next = new;
    return;
}
//群聊的函数
void do_chat(int sockfd, msg_t msg, node_t *p, struct sockaddr_in caddr)
{
    //遍历链表
    while (p->next != NULL)
    {
        p = p->next;
 
        if (memcmp(&(p->caddr), &caddr, sizeof(caddr)) != 0)
        {
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
        }
    }
    return;
}
//退出函数
 
void do_quit(int sockfd, msg_t msg, node_t *p, struct sockaddr_in caddr)
{
    sprintf(msg.text, "%s 以下线", msg.name);
    while (p->next != NULL)
    {
        if ((memcmp(&(p->next->caddr), &caddr, sizeof(caddr))) == 0)
        {
            node_t *dele = NULL;
            dele = p->next;
            p->next = dele->next;
            free(dele);
            dele = NULL;
        }
        else
        {
            p = p->next;
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
        }
    }
    return;
}

客户端

#include<myhead.h>
enum type_t
{
    Login,
    Chat,
    Quit,
};
typedef struct
{
    char type;      
    char name[32];  
    char text[128]; 
} msg_t;
 
int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("Usage ./a.out <ip> <port>\n");
        return -1;
    }
 
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        exit(-1);
    }
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t len = sizeof(serveraddr);
    msg_t msg;
    //先执行登录操作
    printf("请登录:\n");
    msg.type = Login;
    printf("请输入用户名:");
    fgets(msg.name, 32, stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';
    //发送登录消息
    if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len) < 0)
    {
        perror("sendto err");
        exit(-1);
    }
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err");
        exit(-1);
    }
    else if (pid == 0)
    {
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
            {
                perror("recvfrom err");
                return -1;
            }
            printf("[%s]:%s\n", msg.name, msg.text);
        }
    }
    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") == 0)
            {
                msg.type = Quit;
                sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len);
                kill(pid, SIGKILL);
                wait(NULL);
                exit(-1);
            }
            else
            {
                msg.type = Chat;
            }
            //发送消息
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, len);
        }
    }
    close(sockfd);
    return 0;
}

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值