现在的位置: 首页 > 综合 > 正文

多线程tcp聊天模拟

2012年10月12日 ⁄ 综合 ⁄ 共 10268字 ⁄ 字号 评论关闭

/************************************************************
 * 编译方式:
 * gcc -pthread -o chat_server chat_server.c
 *    基于socket的聊天服务器端
 *    设计思路:
 *    客户机提出各种请求,服务器根据不同请求,发送不同的响应.
 *   服务器端采用多线程,为每个连接的客户建立一个服务线程.
 *************************************************************/
#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;
}

 

 

抱歉!评论已关闭.