⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 char_server.c

📁 Linux下基于socket的聊天室
💻 C
字号:
/************************************************************
*    基于socket的聊天服务器端
*    Copyright by Subo, All Right Reserved
*设计思路:
*    客户机提出各种请求,服务器根据不同请求,发送不同的响应.
*   服务器端采用多线程,为每个连接的客户建立一个服务线程.
*关键问题:
*    客户机和服务器之间协议制订
*   多线程访问同一个用户列表的互斥问题
*************************************************************/
#include <pthread.h>
#include <sys/errno.h>    
#include "chat.h"

#define LENGTH_OF_LISTEN_QUEUE    (20)
#define USER_AMOUNT_MAX    (50)

#define NOT_LOGIN    (-1)
#define NOT_IN_USE    (NOT_LOGIN -1)
#define USER_ID_SIZE (ID_SIZE)
typedef struct user{
    char user_ID[USER_ID_SIZE];
    char password[PASSWORD_SIZE];
    int client_socket; 
    //client_socket==NOT_LOGIN,表示没有用户登录,
    //client_socket==NOT_IN_USE,表示没有用户注册,                
}user;
//多线程共享user_table
static user user_table[USER_AMOUNT_MAX];
//访问user_table时要使用的信号量
pthread_mutex_t user_table_mutex;

/************************************************************
*函数名称: init_user_table
*函数执行失败:   当一个报文不能容纳全部用户名称列表时,给出错误提示信息,结束程序
*依赖自定义数据结构:     struct user -- 本文件
*依赖全局变量:          user_table -- 本文件
*************************************************************/
int init_user_table()
{
    if(USER_ID_SIZE*USER_AMOUNT_MAX>MESSAGE_SIZE)
    {
        printf("USER_ID_SIZE*USER_AMOUNT_MAX>MESSAGE_SIZE\n");
        exit(1);
    }    
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        user_table[i].client_socket = NOT_IN_USE;
        bzero(user_table[i].user_ID,OPTION_SIZE);
        bzero(user_table[i].password,OPTION_SIZE);
    }
}
/************************************************************
*函数名称: login
*正常返回值: 登录成功为SUCCEED,登录失败为FAIL
*参数说明: client_socket是服务器同用户正在进行通信的socket
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int login(char * user_ID, char * password, int client_socket)
{
    pthread_mutex_lock(&user_table_mutex);
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if( (strcmp(user_table[i].user_ID,user_ID)==0)
            &&(strcmp(user_table[i].password,password)==0) )
        {
            user_table[i].client_socket = client_socket;
            pthread_mutex_unlock(&user_table_mutex);
            return SUCCEED;
        }
    }
    pthread_mutex_unlock(&user_table_mutex);
    return FAIL;
}
/************************************************************
*函数名称: get_active_user_list
*正常返回值: SUCCEED
*参数说明:   在函数返回数据放置在字符数组user_list_buffer中,
*           在user_list_buffer中,每个用户名称占据USER_ID_SIZE + 1大小.
*           要求user_list_buffer中的数据必须初始化为全'\0'
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int get_active_user_list(char * user_list_buffer)
{
    pthread_mutex_lock(&user_table_mutex);
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(user_table[i].client_socket > NOT_LOGIN)
        {
            memcpy(user_list_buffer, user_table[i].user_ID, USER_ID_SIZE);
            user_list_buffer += USER_ID_SIZE + 1;
        }
    }    
    pthread_mutex_unlock(&user_table_mutex);
    return SUCCEED;
}
/************************************************************
*函数名称: user_register
*正常返回值: 注册成功SUCCEED,注册失败FAIL
*函数执行失败: 注册重复的user_ID,注册失败.
*             如果user_table中没有处于空闲的记录,注册失败.
*参数说明: client_socket是服务器同用户正在进行通信的socket
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int user_register(char * user_ID, char * password, int client_socket)
{
    pthread_mutex_lock(&user_table_mutex);
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(strcmp(user_table[i].user_ID,user_ID)==0)
        {
            pthread_mutex_unlock(&user_table_mutex);
            return FAIL;
        }
    }
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(NOT_IN_USE == user_table[i].client_socket)
        {
            user_table[i].client_socket = NOT_LOGIN;
            memcpy(user_table[i].user_ID,user_ID,USER_ID_SIZE);
            memcpy(user_table[i].password,password,PASSWORD_SIZE);
            pthread_mutex_unlock(&user_table_mutex);
            return SUCCEED;
        }
    }
    pthread_mutex_unlock(&user_table_mutex);
    return FAIL;
}
/************************************************************
*函数名称: look_up_socket
*正常返回值: 服务器与目的用户通信的socket,
*函数执行失败: 没有找到服务器与目的用户通信的socket,返回值为FAIL
*参数说明: receiver是目的用户的ID
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int look_up_socket(char * receiver)
{
    pthread_mutex_lock(&user_table_mutex);
    int socket=0;
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(strcmp(user_table[i].user_ID,receiver)==0)
        { 
            if(user_table[i].client_socket>=0)
            {
                socket = user_table[i].client_socket;
                pthread_mutex_unlock(&user_table_mutex);
                return socket;
            }
        }
    }
    pthread_mutex_unlock(&user_table_mutex);
    return FAIL;
}
/************************************************************
*函数名称: deactive_user
*功能说明: 用于用户登出服务器时,把服务器与用户通信的socket设置为NOT_LOGIN
*正常返回值: SUCCEED
*函数执行失败: 没有找到服务器与用户通信的socket,返回值为FAIL
*参数说明: client_socket是服务器与用户通信的socket
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int deactive_user(int client_socket)
{
    pthread_mutex_lock(&user_table_mutex);
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(user_table[i].client_socket == client_socket)
        {
            user_table[i].client_socket=NOT_LOGIN;
            pthread_mutex_unlock(&user_table_mutex);
            return SUCCEED;
        }
    }
    pthread_mutex_unlock(&user_table_mutex);
    return FAIL;
}
/************************************************************
*函数名称: user_change_register
*正常返回值: 注册成功SUCCEED,注册失败FAIL
*函数执行失败: 注册重复的user_ID,注册失败.
*函数功能的其他说明: 不改变当前用户的登录状态
*参数说明: client_socket是服务器同用户正在进行通信的socket
*依赖自定义数据结构:    struct user -- 本文件
*依赖全局变量:         user_table -- 本文件
*                    user_table_mutex -- 本文件
*************************************************************/
int user_change_register(char * user_ID, char * password, int client_socket)
{
    pthread_mutex_lock(&user_table_mutex);
    int i=0;
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(strcmp(user_table[i].user_ID,user_ID)==0)
        {
            pthread_mutex_unlock(&user_table_mutex);
            return FAIL;
        }
    }
    for(i=0;i<USER_AMOUNT_MAX;i++)
    {
        if(client_socket == user_table[i].client_socket)
        {
            memcpy(user_table[i].user_ID,user_ID,USER_ID_SIZE);
            memcpy(user_table[i].password,password,PASSWORD_SIZE);
            pthread_mutex_unlock(&user_table_mutex);
            return SUCCEED;
        }
    }
    pthread_mutex_unlock(&user_table_mutex);
    return FAIL;
}
/************************************************************
*函数名称: init_server_socket
*功能说明: 初始化服务器用于监听的的socket
*正常返回值: 已经初始化的服务器用于监听的的socket
*函数执行失败: 输出错误信息,退出程序
*************************************************************/
int init_server_socket()
{
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(CHAT_SERVER_PORT);

    int server_socket = socket(AF_INET,SOCK_STREAM,0);
    if( server_socket < 0)
    {
        printf("Create Socket Failed!");
        exit(1);
    }
    
    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
    {
        printf("Server Bind Port : %d Failed!", CHAT_SERVER_PORT); 
        exit(1);
    }

    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
    {
        printf("Server Listen Failed!"); 
        exit(1);
    }
    return server_socket;
}
/************************************************************
*函数名称: process_request
*功能说明: 根据接收到的报文的内容,进行响应的服务.
*          服务类型包括:注册,登录,获取已登录用户列表,向用户发送信息,退出,修改注册信息
*正常返回值: 服务器对用户发回的响应类型
*函数执行失败: 没有检测和处理
*参数说明: client_socket是服务器与用户通信的socket
*         receive_buffer为服务器收到的报文的内容
*依赖自定义数据结构:    chat_package -- chat.h
*************************************************************/
int process_request(int client_socket, char * receive_buffer)
{
    chat_package send_buffer;
    bzero((char*)&send_buffer,BUFFER_SIZE);
    char * user_ID = ((chat_package *)receive_buffer)->from;
    char * password = ((chat_package *)receive_buffer)->password;
    char * receiver = ((chat_package *)receive_buffer)->to;
    printf("Request %d from client\n",((chat_package *)receive_buffer)->type);
    switch(((chat_package *)receive_buffer)->type)
    {
    case REGISTER: 
        send_buffer.type = user_register(user_ID, password, client_socket);
        break;
    case LOGIN:
        send_buffer.type = login(user_ID, password, client_socket);
        break;
    case GET_USER_LIST:
        memcpy(send_buffer.option, USER_LIST, OPTION_SIZE);
        send_buffer.type = get_active_user_list(send_buffer.message);
        break;
    case TALK_TO:
        send_buffer.type = SUCCEED;
        send(client_socket, (chat_package *)&send_buffer,BUFFER_SIZE,0);
        client_socket = look_up_socket(receiver);
        send_buffer.type = TRANSFER;
        memcpy(send_buffer.from, ((chat_package *)receive_buffer)->from, MESSAGE_SIZE);
        memcpy(send_buffer.message, ((chat_package *)receive_buffer)->message, MESSAGE_SIZE);
        break;
    case EXIT: 
        deactive_user(client_socket);
        return send_buffer.type;    
        break;
    case CHANGE:
        send_buffer.type = user_change_register(user_ID, password, client_socket);
    }
    printf("Answer %d to client\n",send_buffer.type);
    send(client_socket, (chat_package *)&send_buffer,BUFFER_SIZE,0);
    return send_buffer.type;    
}
/************************************************************
*函数名称: talk_to_client
*功能说明: 对单独的一个用户的各种请求进行服务,当用户的请求为EXIT时,结束本线程
*函数执行失败: 通信失败时,显示错误信息,结束本线程
*依赖自定义数据结构:    chat_package -- chat.h
*************************************************************/
void * talk_to_client(void * new_server_socket_to_client)
{
    int new_server_socket = (int)new_server_socket_to_client;
    int request = NO_COMMAND;
    while(request!=EXIT)
    {
        chat_package buffer;
        bzero((char*)&buffer,BUFFER_SIZE);
        int length = recv(new_server_socket,(char*)&buffer,BUFFER_SIZE,0);
        if (length < 0)
        {
            printf("Server Recieve Data Failed!\n");
            close(new_server_socket); 
            pthread_exit(NULL);
        }
        if (length==0)
        {
            close(new_server_socket); 
            pthread_exit(NULL);
        }       
        request = process_request(new_server_socket, (char*)&buffer);
    }
    close(new_server_socket); 
    pthread_exit(NULL);       
}

int main(int argc, char **argv)
{
    init_user_table();
    pthread_mutex_init(&user_table_mutex, NULL);
    int server_socket = init_server_socket();

    pthread_t child_thread;
    pthread_attr_t child_thread_attr;
    pthread_attr_init(&child_thread_attr);
    pthread_attr_setdetachstate(&child_thread_attr,PTHREAD_CREATE_DETACHED);
    while (1)
    {
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);
        int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
        if ( new_server_socket < 0)
        {
            printf("Server Accept Failed!\n");
            break;
        }
        if( pthread_create(&child_thread,&child_thread_attr,talk_to_client, (void *)new_server_socket) < 0 )
            printf("pthread_create Failed : %s\n",strerror(errno));
    }
    close(server_socket);
    pthread_attr_destroy(&child_thread_attr);
    pthread_mutex_destroy(&user_table_mutex);
    pthread_exit (NULL);
    return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -