Design of online chat room system based on linux (C language)

The system uses multi-threading to realize communication between different hosts.

Can realize communication between multiple users and can support private chat mode. (Enter your username to enter private chat mode, enter ALL to return to group chat mode)

The exit of a certain client does not affect the use of other clients. All users exit, the server closes the port and ends the process.

Server main thread program block diagram

 Server receiving connection thread program block diagram (not complete, the code was updated later, and the block diagram has not been changed)

  Client main thread program block diagram)

Client message sending and receiving thread program block diagram

  Experimental results

Server side code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <unistd.h>
#define PORT 8080  //1023~65535
#define MAX 100
#define IP "172.17.0.6"
#define USER 20
//设置总的用户连接数
pthread_mutex_t mm;//发送数据的时候加锁,防止阻塞
typedef struct
{
    char buf[100];
}userbuf;//读写缓存
typedef struct sockaddr_in SIN; 
SIN cli_addr[USER];//
int cli_fd[USER];//客户端套接字
int tag=0;//群聊or私聊判断标志位
int count=0;//退出聊天用户数
int user=0;//实际用户数
int sockfd;
typedef struct sockaddr SA;
char siliao[20]="[PRIVATE]";//私聊信息附加显示
userbuf username[USER];//存储用户民信息
userbuf username_plus[USER];//给用户名增加冒号显示
userbuf recvbuf[USER];//服务器端接收缓存
userbuf sendbuf[USER];//服务器端发送缓
int length[USER];
int rank(char a[])
{
    int i;
    for(i=0;i<user;i++)
        if(strncmp(a,username[i].buf,length[i])==0)
            return i;
    return 88;
}

void *talk(void *arg)//每个客户端分配一个线程,实现消息的接收和转发
{
    int num=tag++;//获取线程编号
    int m;
    int flag=88;
    char name[100]={0};
    memset(username[num].buf,0,100);
    length[num]=recv(cli_fd[num],username[num].buf,MAX,0);//接收用户名
    strcpy(username_plus[num].buf,username[num].buf);//
    strcat(username_plus[num].buf,":");
    strcpy(name,username_plus[num].buf);
    while(1)
    {
        memset(recvbuf[num].buf,0,100);//
        recv(cli_fd[num],recvbuf[num].buf,MAX,0);//接收客户端发送的信息
        if(strncmp(recvbuf[num].buf,"quit",4)==0)//如果输入的是quit则表示该客户端结束聊天
        {
            count++;
            close(cli_fd[num]);
            pthread_exit(NULL);//线程结束
        }
        if(strlen(recvbuf[num].buf)==0)
            break;
        int flag=rank(recvbuf[num].buf);
        if(flag==88)
        {
            strcpy(username[num].buf,name);
            strcpy(sendbuf[num].buf,strcat(username[num].buf,recvbuf[num].buf));//用户信息和发送信息组装
            system("date");
            write(1,sendbuf[num].buf,100);
            pthread_mutex_lock(&mm);//发送消息
            for(m=0;m<num;m++)//接收用户消息发给其他用户
            {
                send(cli_fd[m],sendbuf[num].buf,strlen(sendbuf[num].buf),MSG_NOSIGNAL);//其中MSG_NOSIGNAL是为了解决有客户退出后仍然可以发消息的问题
            }
            for(m=num+1;m<user;m++)
            {
                send(cli_fd[m],sendbuf[num].buf,strlen(sendbuf[num].buf),MSG_NOSIGNAL);//发送接收到的消息给其他用户端
            }
            memset(sendbuf[num].buf,0,100);//清空接收的缓存  
            pthread_mutex_unlock(&mm);
        } else{
            while(1){
                memset(recvbuf[num].buf,0,100);//
                recv(cli_fd[num],recvbuf[num].buf,MAX,0);//接收客户端发送的信息
                if(strncmp(recvbuf[num].buf,"quit",4)==0)//如果输入的是quit则表示该客户端结束聊天
                {
                    count++;
                    close(cli_fd[num]);
                    pthread_exit(NULL);//线程结束
                }
                if(strncmp(recvbuf[num].buf,"ALL",3)==0)
                    break;
                strcpy(username[num].buf,name);
                strcat(username[num].buf,siliao);
                strcpy(sendbuf[num].buf,strcat(username[num].buf,recvbuf[num].buf));//用户信息和发送信息组装
                pthread_mutex_lock(&mm);
                send(cli_fd[flag],sendbuf[num].buf,strlen(sendbuf[num].buf),0);
                pthread_mutex_unlock(&mm);
            }       
        }
    }
    close(cli_fd[num]);
    pthread_exit(NULL);
}

void *user_connect(void *arg)//专门使用一个线程来处理连接,动态处理用户的连接
{
    pthread_t tid[USER];//为每个客户端分配一个线程
    sockfd=socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket create failure\r\n");
        return 0;
    }
    //2、创建结构,初始化数据 struct sockaddr      使用 struct sockaddr_in
    SIN ser_addr;//服务器端
    ser_addr.sin_family = AF_INET;//选择ipv4协议族
    ser_addr.sin_port=htons(PORT);  //端口号要转换端绪,从小端绪转换从大端绪
    ser_addr.sin_addr.s_addr=inet_addr(IP);//十进制的字符ip转换成网端数据格式

    //3、绑定端口号和IP地址  bind()
    if(bind(sockfd,(SA *)&ser_addr,sizeof(ser_addr)) == -1)
    {
        perror("bind failure\r\n");
        return 0;
    }

    //4、创建监听 listen() 数量
    if(listen(sockfd, USER) == -1)
    {
        perror("listen failure\r\n");
        return 0;
    }
    printf("waiting connect\r\n");
    int i;
    for(i=0;i<USER;i++)
        memset(&cli_addr[i],0,sizeof(cli_addr[i]));
    while(1)
    {
        int len=sizeof(cli_addr[user]);
        cli_fd[user]=accept(sockfd,(SA *)&cli_addr[user],&len);//接受客户端的连接
        if(cli_fd[user] == -1)
        {
            perror("wait failure\r\n");
            return 0;
        }
        else{
            printf("THE %d USER CONNECT SUCCESS!\n",user+1);
            pthread_create(&tid[user],NULL,talk,NULL);
        }
        user++;
    }
}
int main(int argc,char *argv[])
{
    //1、创建套接字文件,返回套接字文件描述符 socket()
    pthread_mutex_init(&mm, NULL);
    pthread_t CON;
    pthread_create(&CON,NULL,user_connect,NULL);//分配连接的线程
    sleep(30);//30s等待连接
    while(count!=user);
    printf("-------**********************************-------\n");
    printf("-------WELCOME JOIN DADONG TALK ROOM AGAIN-------\n");
    //7、关闭文件描述符 close(
    close(sockfd);
    return 0;
}

Client code:

#include <stdio.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netinet/ip.h>
#define IP "172.17.0.6"
#define PORT 8080
#define MAX 100
int final=0;
// 创建socket套接字文件,并连接
// 接受数据  client 客户端
typedef struct sockaddr_in SIN;
typedef struct sockaddr SA;
SIN ser_addr;
int sockfd;
void *message(void *arg)
{
    printf("thread creat success!\n");
    char buf[100];
    int signal;
    while(1)
    {
        if(final==1)
            break;
        memset(buf,0,100);
        signal=recv(sockfd,buf,MAX,0);
        if(signal!=0){
            system("date");
            write(1,buf,strlen(buf));
            memset(buf,0,100);}
    }
    pthread_exit(NULL);
}
int main(int argc,char *argv[])
{
    //1、创建套接字文件,返回套接字文件描述符 socket()
    sockfd=socket(AF_INET, SOCK_STREAM, 0);
    pthread_t tid;
    if(sockfd == -1)
    {
        perror("socket create failure\r\n");
        return 0;     }

    //2、创建结构,初始化数据 struct sockaddr      使用 struct sockaddr_in
    SIN ser_addr;
    ser_addr.sin_family = AF_INET;//选择ipv4协议族  
    ser_addr.sin_port=htons(PORT);  //端口号要转换端绪,从小端绪转换从大端绪
    ser_addr.sin_addr.s_addr=inet_addr(IP);//十进制的字符ip转换成网端数据格式 服务端IP
    int len=sizeof(ser_addr);
    if(connect(sockfd,(SA *)&ser_addr,len)==-1)
    {
        perror("connect failure\r\n");
        return 0;
    }else
    {
        printf("WELCOME TO DADONG TALK ROOM!\r\n");   
        printf("Please sign you name:");
        char temp[20]={0};
        memset(temp,0,100);
        scanf("%s",temp);
        send(sockfd,temp,strlen(temp),0);//发送信息
        pthread_create(&tid,NULL,message,NULL);
    }
    char wbuf[100]={0};
    while(1)
    {
        memset(wbuf,0,100);
        read(0,wbuf,100);
        if(strncmp(wbuf,"quit",4)==0)
        {
            send(sockfd,wbuf,strlen(wbuf),0);
            final=1;
            close(sockfd);
            return 0;
        }
        send(sockfd,wbuf,strlen(wbuf),MSG_NOSIGNAL);
        memset(wbuf,0,100);
        usleep(20);
    }
    close(sockfd);
    return 0;
}        

Guess you like

Origin blog.csdn.net/yoonaanna/article/details/123434301