基于C语言的UDP聊天室项目——网络编程(含源码)

一、UDP网络编程———准备阶段

1.1项目要求

    利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件。

1.2实现功能

登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。

聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。

退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。

实现思路:

(1) 服务器存储客户端的地址可以采用:数据结构可以选择线性数据结构。

typedef struct
{
    char type;      //消息类型 L C Q
    char id[32];    //用户id
    char text[128]; //消息内容
} msg_t;

//链表节点
typedef struct node_t
{
    struct sockaddr_in caddr;
    struct node_t *next; 
} list;

(2) 客户端如何同时处理发送和接收的问题可以使用多任务来同时处理,使用多进程或者多线程来处理。

1.3程序流程图

 

 二、代码实现(源码)

2.1服务器端    seaver.c

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include<pthread.h>
typedef struct
{
    char type;      //消息类型 L C Q
    char id[32];    //用户id
    char text[128]; //消息内容
} msg_t;

//链表节点
typedef struct node_t
{
    struct sockaddr_in caddr;
    struct node_t *next; 
} list;

 struct sockaddr_in saddr, caddr;

//创建头节点 
list *list_create(void)
{
    list *p = (list *)malloc(sizeof(list));
    if (p == NULL)
    {
        perror("malloc err.\n");
        return NULL;
    }
    p->next = NULL;
    return p;
}

void login(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);
void chat(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);
void quit(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr);

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("usage:./a.out <port>\n");
        return -1;
    }

    int sockfd;
    struct sockaddr_in saddr, caddr;
    socklen_t len = sizeof(caddr);
    msg_t msg;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("sock err.\n");
        return -1;
    }
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_port = htons(atoi(argv[1]));
    bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    printf("bind ok!\n");

    list *p = list_create();
    int recvbyte;
    pthread_t tid;
    pthread_create(&tid,NULL,handler,&sockfd);
    pthread_detach(tid);

    while (1)
    {
        struct timeval val={2,0};
        setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&val,sizeof(val));
        recvbyte = recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len);
        if (recvbyte < 0)
        {
            perror("recvfrom err.\n");
            return -1;
        }

        if (msg.type == 'L') //登入
        {
            login(sockfd, msg, p, caddr);
        }
        else if (msg.type == 'C') //聊天
        {
            chat(sockfd, msg, p, caddr);
        }
        else if (msg.type == 'Q') //退出
        {
            printf("type :%c\n", msg.type);
            printf("ip:%s pord:%d id:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), msg.id);
            printf("text:%s\n", msg.text);
            quit(sockfd, msg, p, caddr);
        }
    }
    close(sockfd);
    return 0;
}

void login(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
    list *new = NULL;
    sprintf(msg.text, "login");

    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);
    }
    //将新用户信息保存 尾插到链表
    new = (list *)malloc(sizeof(list));
    new->caddr = caddr;
    new->next = NULL;
    p->next = new;
    return;
}

void chat(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
    int n;
    while (p->next != NULL)
    {
        p = p->next;
        n = memcmp(&(p->caddr), &caddr, sizeof(caddr));
        if (n != 0)
        {
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
        }
    }
    return;
}

void quit(int sockfd, msg_t msg, list *p, struct sockaddr_in caddr)
{
    list *dele;
    sprintf(msg.text, "logout");
    while (p->next != NULL)
    {
        if ((memcmp(&(p->next->caddr), &caddr, sizeof(caddr))) == 0) //找要释放的前一个节点
        {
            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;
}
void * handler(void *arg)
{
    int socketfd = *(int *)arg;
    msg_t msg_s;
    strcpy(msg_s.id,"seaver");
    while (1)
    {
        scanf("%[^\n]s",msg_s);
        getchar();
        sendto(socketfd,msg_s.text,sizeof(msg_s),0,(struct sockaddr *)caddr,sizeof(caddr));
    }
    
}

2.2用户端  client.c

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

typedef struct
{
    char type;      //消息类型 L C Q
    char id[32];    //用户id
    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;
    msg_t msg;
    struct sockaddr_in caddr;
    socklen_t len = sizeof(caddr);
    char buf[128];
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("sock err.\n");
        return -1;
    }
    caddr.sin_family = AF_INET;
    caddr.sin_addr.s_addr = inet_addr(argv[1]);
    caddr.sin_port = htons(atoi(argv[2]));

    msg.type = 'L';
    printf("please imput your id\n");
    scanf("%[^\n]s", msg.id);
    getchar();

    sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.\n");
        return -1;
    }
    else if (pid == 0) //子进程循环发送消息
    {
        while (1)
        {

            scanf("%[^\n]s", msg.text);
            getchar();
            //printf("%s\n", msg.text);

            if (strncmp(msg.text, "quit", 4) == 0)
            {
                msg.type = 'Q';
                sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);
                kill(getppid(), SIGKILL);
                wait(NULL);
                exit(-1);
            }
            else
            {
                msg.type = 'C';
            }
            //printf("sssssshu\n");
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, len);
        }
    }

    else //父进程循环接受消息
    {
        int recvbyte;
        while (1)
        {
            recvbyte = recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL);
            if (recvbyte < 0)
            {
                perror("recvfrom err.\n");
                return -1;
            }
            printf("%s:%s\n", msg.id, msg.text);
        }
        wait(NULL);
    }
    close(sockfd);
    return 0;
}

2.3运行结果 

猜你喜欢

转载自blog.csdn.net/m0_65821435/article/details/129801677