Linux多人羣聊系統(簡單多線程服務器)


 
 

一、要求

  1. 通過一個服務器實現最多5個客戶之間的信息羣發。
  2. 服務器顯示客戶的登錄與退出;
  3. 客戶連接後首先發送客戶名稱,之後發送羣聊信息;
  4. 客戶輸入bye代表退出,在線客戶能顯示其他客戶的登錄於退出。

 

二、提示

1. 服務器端

主線程:
  定義一個全局客戶信息表ent,每個元素對應一個客戶,存儲:socket描述符、客戶名、客戶IP、客戶端口、狀態(初值爲0)。主線程循環接收客戶連接請求,在ent中查詢狀態爲0的元素,如果不存在狀態爲0的元素(即連接數超過最大連接數),向客戶發送EXIT標誌; 否則,修改客戶信息表中該元素的socket描述符、客戶IP、客戶端口號,狀態爲1(表示socket可用);同時創建一個通信線程並將客戶索引號index傳遞給通信線程。

通信線程:
  首先向客戶端發送OK標誌;循環接收客戶發來信息,若信息長度爲0,表示客戶端已關閉,向所有在線客戶發送該用戶退出;若信息爲用戶名,修改全局客戶信息表ent中index客戶的用戶名name,並顯示該用戶登錄;若信息爲退出,修改全局客戶信息表ent中index客戶狀態爲0,並顯示該用戶退出,終止線程;同時查詢全局客戶信息表ent,向狀態爲1的客戶發送接收的信息。

2. 客戶端

  根據用戶從終端輸入的服務器IP地址及端口號連接到相應的服務器;連接成功後,接收服務端發來的信息,若爲EXIT,則達到最大用戶量,退出;若爲OK,可以通訊,首先先發送客戶名稱;主進程循環從終端輸入信息,並將信息發送給服務器;當發送給服務器爲bye後,程序退出。同時創建一個線程負責接收服務器發來的信息,並顯示,當接收的長度小於等於0時終止線程;

 

三、程序

客戶端與服務端的頭文件:

ifndef _GCS_H
#define _GCS_H
#include <netinet/in.h>


#define MSGLEN  1024

#define OK   1
#define EXIT 2
#define MSG  3
#define USER 4



struct CLIENTMSG {
    int op;
    char user[20];
    char buf[MSGLEN];
};

struct CLIENTS {
    int sockfd;
    int port;
    char user[20];
    struct sockaddr_in client;
    int stat;
};
struct CLIENTS ent[5];



#endif

服務端:

#include <stdio.h>  
#include <stdlib.h>     
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
#include <netinet/in.h>  
#include <sys/types.h>  
#include <unistd.h> 
#include <fcntl.h>
#include <sys/ipc.h>
#include <pthread.h>
#include "group_chat_system.h"

extern struct CLIENTS ent[5];


void* func(void *arg);
void sever_process(int index);


int main(int argc, char **argv)
{
    int sever_fd,client_fd;
    struct sockaddr_in sever,client;
    char ip[20];
    int port,clientlen;
    unsigned char i,index;
    struct CLIENTMSG clientMSG;
    pthread_t tid;
    int *arg;

    for(i=0; i<5; i++) {
        ent[i].stat = 0;
    }

    /***************創建服務器sockfd************/
    if((sever_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(errno);
    }

    /***************輸入ip與端口*****************/
    printf("please input ip:");
    scanf("%s", ip);
    printf("\nplease input port:");
    scanf("%d", &port);
    printf("\n");

    /******************bind***********************/
    bzero(&sever, sizeof(sever));
    sever.sin_family = AF_INET;
    sever.sin_port   = htons(port);
    sever.sin_addr.s_addr= inet_addr(ip);
    if(bind(sever_fd, (struct sockaddr*)&sever, sizeof(struct sockaddr)) == -1) {
        perror("bind error");
        exit(errno);
    }

   /******************listen***********************/
   if(listen(sever_fd, 5) == -1) {
        perror("listen error");
        exit(errno);
   }
    
   while(1) {
    clientlen = sizeof(client);
    if((client_fd = accept(sever_fd, (struct sockaddr*)&client, &clientlen)) == -1) {
        perror("accept error");
        exit(errno);
    }
    printf("accept ok\n");
    index = 5;
    for(i=0; i<5; i++) {
        if(ent[i].stat ==0){
            index = i;
            break;
      }
    }

    if(index <= 4) {
        printf("client_fd : %d\n", client_fd);
        ent[index].sockfd = client_fd;
        ent[index].port   = port;
        ent[index].client = client;
        ent[index].stat   = 1;
        arg = malloc(sizeof(int));
        *arg = index;
        pthread_create(&tid, NULL, func, (void*)arg);
    } else {
        printf("the client already has 5\n");
        bzero(&clientMSG, sizeof(clientMSG));
        clientMSG.op = EXIT;
        send(client_fd, &clientMSG, sizeof(clientMSG), 0);
        close(client_fd);
    }

   }
    close(sever_fd);
    return 0;
}


void *func(void *arg) 
{
    int *info;
    info = (int *)arg;
    sever_process(*info);
    pthread_exit(NULL);
}

void sever_process(int index)
{
    struct CLIENTMSG sendMSG;
    struct CLIENTMSG recvMSG;
    int len,i;

    /*首先發送邋OK標誌*/
    sendMSG.op = OK;
    send(ent[index].sockfd, &sendMSG, sizeof(sendMSG), 0);

    while(1) {
        bzero(&sendMSG, sizeof(sendMSG));
        bzero(&recvMSG, sizeof(recvMSG));

        len = recv(ent[index].sockfd, &recvMSG, sizeof(recvMSG), 0);
        if(len == 0) {
            sendMSG.op = EXIT;
            for(i=0; i<5; i++) {
                if(ent[i].stat == 1)
                    send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0);
            }
        }
        if(len > 0) {
            if(recvMSG.op == USER) {
                bcopy(recvMSG.user, ent[index].user, strlen(recvMSG.user));
                printf("user %s login form ip: %s   port: %d\n", ent[index].user, inet_ntoa(ent[index].client.sin_addr), ntohs(ent[index].client.sin_port));
                sendMSG.op = USER;
            }
            if(recvMSG.op == EXIT) {
                //printf("recv exit\n");
                sendMSG.op = EXIT;
                bcopy(ent[index].user, sendMSG.user, strlen(recvMSG.user));
                for(i=0; i<5; i++) {
                    if(ent[i].stat == 1)
                        send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0);
                }
                break;
            }
            if(recvMSG.op == MSG) {
                //printf("recv msg\n");
                //printf("%s: %s", recvMSG.user,recvMSG.buf);
                sendMSG.op= MSG;
            }    
            bcopy(recvMSG.user, sendMSG.user, strlen(recvMSG.user));
            bcopy(recvMSG.buf, sendMSG.buf, strlen(recvMSG.buf));
            for(i=0; i<5; i++) {
                //printf("\n%d: %s\n", sendMSG.op, sendMSG.user);
                if(ent[i].stat == 1) {
                    if(strncmp(ent[i].user, sendMSG.user, strlen(sendMSG.user)))
                        send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0);
                }
                    
            }    
        }
    }

}

客戶端:

#include <stdio.h>  
#include <stdlib.h>     
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
#include <netinet/in.h>  
#include <sys/types.h>  
#include <unistd.h> 
#include <fcntl.h>
#include <sys/ipc.h>
#include <pthread.h>
#include "group_chat_system.h"


struct client_arg {
    int sockfd;
    struct CLIENTMSG clientMSG;
};
struct client_arg *arg;

void *func(void* arg);
void client_process(int sockfd, struct CLIENTMSG clientMSG);

int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in sever;
    char ip[20];
    unsigned int port,len;
    struct CLIENTMSG sendMSG;
    pthread_t tid;

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(1);
    }

    printf("please input ip:");
    scanf("%s", ip);
    printf("\nplease input port:");
    scanf("%d", &port);
    printf("\n");

    bzero(&sever, sizeof(sever));
    sever.sin_family = AF_INET;
    sever.sin_port = htons(port);
    inet_aton(ip, &sever.sin_addr);
    if(connect(sockfd, (struct sockaddr*)&sever, sizeof(struct sockaddr)) == -1) {
        perror("connect error");
        exit(errno);
    }

    len = recv(sockfd, &sendMSG, sizeof(sendMSG), 0);
    if(len > 0){
        if(sendMSG.op == EXIT) {
            printf("exceed the numble of users\n");
        }
        if(sendMSG.op == OK) {
            bzero(&sendMSG, sizeof(sendMSG));
            printf("please input client name: ");
            fgets(sendMSG.user, MSGLEN, stdin);
            //scanf("%s", sendMSG.user);
            sendMSG.op = USER;
            send(sockfd, &sendMSG, sizeof(sendMSG), 0);
            arg = (struct client_arg*)malloc(sizeof(struct client_arg));
            arg->sockfd = sockfd;
            pthread_create(&tid, NULL, func, (void*)arg);

            while(1) {
//                bzero(&sendMSG, sizeof(sendMSG));
                sendMSG.op = MSG;
                //printf("waiting...");
                fgets(sendMSG.buf, MSGLEN, stdin);
                //scanf("%s", sendMSG.buf);
                //printf("%s: %s  %d\n", sendMSG.user,sendMSG.buf, sendMSG.op);
                if(!strncasecmp(sendMSG.buf, "bye", 3)) {
                    sendMSG.op = EXIT;
                    send(sockfd, &sendMSG, sizeof(sendMSG), 0);
                    break;
                }
                send(sockfd, &sendMSG, sizeof(sendMSG), 0);
            }
        }
        pthread_cancel(tid);
    }
    close(sockfd);
    return 0;
}

void *func(void* arg)
{
    struct client_arg *info;
    info = (struct client_arg*)arg;
    client_process(info->sockfd, info->clientMSG);
    free(arg);
    pthread_exit(NULL);
}

void client_process(int sockfd, struct CLIENTMSG clientMSG)
{
    int len;
    while(1) {
        bzero(&clientMSG, sizeof(clientMSG));
        len = recv(sockfd, &clientMSG, sizeof(clientMSG), 0);
        if(len > 0) {
            if(clientMSG.op==USER){
                printf("the user %s is login.\n",clientMSG.user);
             }else if(clientMSG.op == EXIT){
                 printf("the user %s is logout.\n",clientMSG.user);
             }else if(clientMSG.op == MSG){
                printf("%s: %s\n",clientMSG.user,clientMSG.buf);
            }
        }
    }
}

makefile:

main:sever.o client.o
        gcc sever.o -o sever -lpthread
        gcc client.o -o client -lpthread
sever.o:sever.c
        gcc -c sever.c group_chat_system.h
        gcc -c client.c group_chat_system.h
clean:
        rm *.o sever client

 

四、測試

運行服務端:
在這裏插入圖片描述
運行客戶端:(3個)
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
測試成功。

 
 
 
 
 
 
 
關注公衆號"小敗日記",搬磚過程遇到的問題,大家一起探討,資源共享

小敗日記公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章