多人聊天室程序的问题

多人聊天室程序(3人一组)
linux下 用c语言实现简单的网络聊天室程序,该程序主要包含两部分,分别是
服务器程序和客户端程序。
一、服务器主要功能:
(1)
服务器可以在特定的端口监听客户端的连接请求。
(2)
客户端连接成功后,向客户端发送登录成功信息。
(3)
多个客户端可以同时登录。
(4)
可以向在线的所有客户端发送消息。
(5)
通过日志文件记录客户端登录、退出信息。
二、客户端主要功能:
(1)
能连接到服务器。
(2)
能和服务器进行数据交互。

提供参考实例,【Linux 系统下的简单聊天室(C语言实现) 】,链接:https://www.cnblogs.com/zhengguorong/p/15734236.html
【该实例可正常运行客户端和服务端】

这个课题任务量可不小啊,以下代码能实现服务器和客户端的连接和通信,给你参考。
如果有帮助,望采纳!

server.c

//server.c
/*
    广播:服务器 p 接收客户端发送来的消息,再将该消息广播给其他客户端
    点播:服务器 p 接收客户端发送来的消息,再将消息转发给制定ip的客户端
    
    运行格式:  ./server  端口号
                                                                        */
                                                                        
#include "myhead.h"


struct clientlist *head;

struct clientlist
{
    int fd;
    char ip[16];
    unsigned short port;
    struct clientlist *next;
};

//节点初始化
struct clientlist *node_init(void)
{
    struct clientlist *p;
    
    p = (struct clientlist *)malloc(sizeof(struct clientlist));
    if(p == NULL)
    {
        perror("malloc failure");
        return NULL;
    }
    memset(p, 0, sizeof(struct clientlist));
    p->next = NULL;
    
    return p;
}

//插入节点
void insert_node(struct clientlist *new)
{
    struct clientlist *p;
    if(new == NULL)
    {
        printf("new is NULL!\n");
        return;
    }
    p = head;
    while(p->next != NULL)
    {
        p = p->next;
    }
    new->next = p->next;
    p->next = new;
}

//删除节点
void del_node(struct clientlist *del)
{
    struct clientlist *p;
    
    if(head == NULL || del == NULL)
    {
        printf("head or del is NULL!\n");
        return ;
    }        
    p = head;    
    while(p->next != del)
    {
        p = p->next;
    }    
    p->next = del->next;
    del->next = NULL;    
}

//接收客户端的消息
void *recv_msg(void *arg)
{
    int ret;
    char *c;
    char buf[6] = {0};
    char msg[100] = {0};
    char msg1[100] = {0};
    struct clientlist *client = (struct clientlist *)arg;
    struct clientlist *p;
    
    //接收客户端的消息
    while(1)
    {
        ret = recv(client->fd, msg, sizeof(msg), 0);
        if(ret == 0)    //客户端退出了
        {
            del_node(client);
            free(client);
            break;
        }
        printf("client %d : %s\n", client->port, msg);
        p = head->next;
        while(p != NULL)
        {
            if(p->fd != client->fd && strncmp(msg, "broadcast", 9) == 0)    //广播
            {
                send(p->fd, msg, strlen(msg), 0);
            }
            else    //点播
            {
                c = strchr(msg, ':');    //获取字符串中第一次出现':'的地址
                /* if(p->fd != client->fd && strncmp(msg, client->ip, (int)(c-msg)) == 0)    //验证ip
                {
                    send(p->fd, msg+(int)(c-msg), strlen(msg)-(int)(c-msg), 0);
                } */
                sprintf(buf, "%d", p->port);    //将端口号由int转为char
                if(p->fd != client->fd && strncmp(buf, msg, (int)(c-msg)) == 0)    //验证端口号
                {
                    sprintf(msg1, "%hu : %s", client->port, msg+(int)(c-msg)+1);
                    send(p->fd, msg1, strlen(msg1), 0);
                    break;
                }
            }            
            p = p->next;
            memset(buf, 0, sizeof(buf));
        }
        memset(msg, 0, sizeof(msg));
        memset(msg1, 0, sizeof(msg1));
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    int i = 0;
    int sockfd, new_sockfd, addr_len, ret;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    pthread_t tid[100];
    struct clientlist *new = NULL;
    
    head = node_init();
    
    addr_len = sizeof(struct sockaddr_in);
    
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));    //小端序转大端序
    
    //INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);    //自动获取本地ip,INADDR_ANY 只能用于htonl()
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("bind fail");
        return -1;
    }
    
    ret = listen(sockfd, 5);
    if(ret == -1)
    {
        perror("listen fail");
        return -1;
    }
    
    while(1)
    {
        new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
        if(new_sockfd == -1)
        {
            perror("accept fail");
            return -1;
        }
        printf("client  ip  : %s\n", inet_ntoa(client_addr.sin_addr));    //大端转小端
        printf("client port : %hu\n", ntohs(client_addr.sin_port));
        new = node_init();
        new->fd = new_sockfd;
        strcpy(new->ip, inet_ntoa(client_addr.sin_addr));
        new->port = ntohs(client_addr.sin_port);
        insert_node(new);
        
        pthread_create(&tid[i], NULL, recv_msg, new);
        
        i++;
    }    
    
    close(sockfd);
    close(new_sockfd);
    
    return 0;
}

client.c

//client.c
/*
    运行格式:
                ./client  自身端口号  服务器端口号

    输入格式:
                广播 --- broadcast:xxxxxx
                点播 --- 对方端口号:xxxxxx
                                                    */

#include "myhead.h"

int sockfd;
pthread_t tid;

void fun(int sig)
{
    pthread_cancel(tid);
    close(sockfd);
    exit(0);
}

void *recv_msg(void *arg)
{
    int ret;
    char msg[100] = {0};
    
    //接收服务器的消息
    while(1)
    {
        ret = recv(*((int *)arg), msg, sizeof(msg), 0);
        if(ret == 0)
                break;
        printf("%s\n", msg);
        memset(msg, 0, sizeof(msg));
    }
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    int ret;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    char msg[100] = {0};
    
    signal(SIGINT, fun);
    
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));    //小端序转大端序
    server_addr.sin_addr.s_addr = inet_addr("192.168.91.26");
    
    memset(&client_addr, 0, sizeof(struct sockaddr_in));
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(atoi(argv[1]));    //小端序转大端序
    client_addr.sin_addr.s_addr = inet_addr("192.168.91.26");
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket fail");
        return -1;
    }
    
    ret = bind(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("bind fail");
        return -1;
    }
    
    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("connect fail");
        return -1;
    }
    
    pthread_create(&tid, NULL, recv_msg, &sockfd);
    
    //给服务器发送消息
    while(1)
    {
        scanf("%s", msg);
        send(sockfd, msg, strlen(msg), 0);
        memset(msg, 0, sizeof(msg));
    }
    
    return 0;
}

myhead.h

//myhead.h
#ifndef __MYHEAD_H__
#define __MYHEAD_H__


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <pthread.h>
#include <math.h>
#include <time.h>
#include <utime.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdarg.h>
#include <pwd.h>
#include <grp.h>
#include <utmp.h>

#endif

C语言实现--基于UDP的多人在线聊天室
如有帮助,望采纳,辛苦写的

服务端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
 
//消息类型
enum type_t
{
    Login = 1, //登录
    Chat,      //聊天
    Quit,      //退出
};
 
//定义描述消息结构体
typedef struct msg_t
{
    int type;       //消息类型:登录  聊天  退出
    char name[32];  //姓名
    char text[128]; //消息正文
} MSG_t;
 
//链表的节点结构体
typedef struct node_t
{
    struct sockaddr_in addr; //数据域
    struct node_t *next;     //指针域
} link_t;
 
link_t *createLink(void);
void client_login(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
void client_chat(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
void client_quit(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg);
 
int main(int argc, char const *argv[])
{
    int sockfd;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
 
    //填充服务器的ip和port
    struct sockaddr_in serveraddr, clientaddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
    socklen_t len = sizeof(clientaddr);
 
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    MSG_t msg;
    //创建子进程,父进程接收客户端的信息并处理,子进程转发消息
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        //创建一个空的有头单向链表
        link_t *p = createLink();
        while (1) //收到客户端的请求,处理请求
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &len) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            switch (msg.type)
            {
            case Login: //登录
                client_login(sockfd, p, clientaddr, msg);
                break;
            case Chat:
                client_chat(sockfd, p, clientaddr, msg);
                break;
            case Quit:
                client_quit(sockfd, p, clientaddr, msg);
                break;
            }
        }
    }
    else
    {
        while (1) //服务器发通知
        {
            msg.type = Chat;
            //给结构体中的数组成员变量赋值,一般使用strcpy进行赋值
            strcpy(msg.name, "server");
            //获取终端输入
            fgets(msg.text, sizeof(msg.text), stdin);
            //解决发送信息时,会将换行符发送过去的问题
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            //将信息发送给同一局域网的其他客户端
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
                   sizeof(serveraddr));
        }
    }
    //程序结束时,关闭套接字描述符
    close(sockfd);
    return 0;
}
 
//链表函数 -- 创建一个空的有头单向链表
link_t *createLink(void)
{
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if (p == NULL)
    {
        perror("malloc head node err.");
        return NULL;
    }
    p->next = NULL;
    return p;
}
 
//登录函数 -- 将客户端的clientaddr保存到链表中,循环链表告诉其他用户谁登录了
void client_login(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
    //1.告诉其他用户登录的新用户是谁
    strcpy(msg.name,"server"); //发送消息的人是服务端
    sprintf(msg.text, "%s login!", msg.name); //服务端发送新登陆的用户名
    //循环发送给之前已经登陆的用户
    while (p->next != NULL)
    {
        p = p->next;
        sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
    //上面的代码运行结束后,此时链表指针已经指向了最后一个用户
    //2.创建一个新节点保存新连接的客户端地址 ,连接到链表结尾
    link_t *pnew = (link_t *)malloc(sizeof(link_t));
    if (pnew == NULL)
    {
        perror("malloc new node err.");
    }
    //初始化
    pnew->addr = clientaddr;
    pnew->next = NULL;
    //链接  p是最后一个节点的地址
    p->next = pnew;
}
 
//聊天信息发送函数 -- 将消息转发给所有的用户,除去发送消息的自己
void client_chat(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
    //从链表头开始遍历
    while (p->next != NULL)
    {
        p = p->next;
        //memcmp函数,比较内存区域a和b的前n个字节
        //参数--区域a,区域b,比较字节数n
        //返回值--a<b返回负数,a=b返回0,a<b返回正数
        if(memcmp(&(p->addr), &clientaddr, sizeof(clientaddr)) != 0)
        {
            //只要判断出用户地址和发送消息的用户地址不同,就将消息发送给该用户
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->addr),
                   sizeof(p->addr));
        }
    }
    //在服务器中打印发送的消息
    printf("%s said %s\n", msg.name, msg.text);
}
 
//退出函数 -- 将客户端的clientaddr从链表中删除,循环链表告诉其他用户谁退出了
void client_quit(int sockfd, link_t *p, struct sockaddr_in clientaddr, MSG_t msg)
{
    link_t *pdel = NULL;
    //从头开始遍历查找要删除的节点
    while (p->next != NULL)
    {
        //如果循环到的地址是要删除的用户,则删除
        if (memcmp(&(p->next->addr), &clientaddr, sizeof(clientaddr)) == 0)
        {
            //删除指定用户
            pdel = p->next;
            p->next = pdel->next;
 
            free(pdel);
            pdel = NULL;
        }
        else
        {
            //如果不是要删除的用户,则向其发送指定用户要删除的消息
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->next->addr),
                   sizeof(p->next->addr));
            p = p->next;
        }
    }
}

客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
 
//消息类型
enum type_t
{
    Login = 1, //登录
    Chat,      //聊天
    Quit,      //退出
};
 
//定义描述消息结构体
typedef struct msg_t
{
    int type;       //消息类型:登录  聊天  退出
    char name[32];  //姓名
    char text[128]; //消息正文
} MSG_t;
 
 
int main(int argc, char const *argv[])
{
    int sockfd;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -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]);
 
    MSG_t msg;
    //UDP客户端不用bind地址,可以直接发送自己的登陆消息
    msg.type = Login; //登录
    printf("please input login name>>");
    fgets(msg.name, sizeof(msg.name), stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';
 
    sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
           sizeof(serveraddr));
    //创建父子进程:父进程收消息 子进程发送消息
    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';
            //判断发送的消息是否为“quit”退出消息
            if (strncmp(msg.text, "quit", 4) == 0)
            {
                msg.type = Quit;
                sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
                       sizeof(serveraddr));
                //杀死父进程
                kill(getppid(), SIGKILL);
                //退出循环,子进程结束
                break;
            }
            else
            {
                msg.type = Chat;
                sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr,
                       sizeof(serveraddr));
            }
        }
    }
    else
    {
        while (1) //收
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            printf("%s said %s\n", msg.name, msg.text);
        }
    }
    close(sockfd);
    return 0;
}