Implementación de inicio de sesión y registro de salas de chat basada en sistema Linux (03)

En el artículo anterior, ya hablamos sobre cómo construir un modelo de servidor multiproceso que pueda admitir que varios clientes se conecten al servidor al mismo tiempo. En este artículo, implementaremos varios clientes, cómo registrar información con el servidor e implementar la función de inicio de sesión.

estructura de datos

Continúe agregando funciones al código de ejemplo del artículo anterior. Para implementar las funciones de registro e inicio de sesión, debemos permitir que el servidor y el cliente envíen y reciban señales en un formato unificado al intercambiar paquetes de datos. Formato de señalización

//C/S通信的结构体
struct protocol{
 int cmd;     //命令
 int state;  //存储命令返回信息
 char name[32]; //用户名
 char data[64]; //数据
};

Tipo de comando: Los comandos en el formato de señalización se definen de la siguiente manera:

/*cmd*/
#define BROADCAST 0X00000001   //广播数据
#define PRIVATE 0X00000002     //私聊
#define REGISTE 0X00000004     //注册账号
#define LOGIN 0X00000008       //登录
#define ONLINEUSER     0X00000010 //显示在线用户
#define LOGOUT     0X00000020    //退出

El servidor de información del usuario en línea necesita mantener toda la información del usuario y necesita saber si el usuario está en línea y registrado.

//在线用户 
struct ONLINE{
 int fd;  //-1:该用户下线   >0:该用户已经登录,对应的套接字
 int flage; //-1 该条目没有用户信息  1:该条目有用户注册信息
 char name[32]; //注册的用户名字
 char passwd[32];  //用户名密码
}; 
struct ONLINE online[MAX_USER_NUM];

La información del cliente registrado debe almacenarse en el servidor. En aras de la simplicidad, no utilizamos el almacenamiento de la base de datos por el momento. Solo definimos una matriz global para guardar la información del cliente y solo permitimos que 64 clientes inicien sesión.

Valor de retorno del resultado del procesamiento del servidor

/*return code*/
#define OP_OK    0X80000000         //操作成功
#define ONLINEUSER_OK    0X80000001  //显示在线用户,未结束
#define ONLINEUSER_OVER  0X80000002  //显示在线用户,已经发送完毕
#define NAME_EXIST 0X80000003       //注册信息,用户名已经存在
#define NAME_PWD_NMATCH 0X80000004 //登录时,输入的用户名密码不对
#define USER_LOGED 0X80000005     //登录时,提示该用户已经在线
#define USER_NOT_REGIST 0X80000006  //登录时,提示用户没有注册

Diagrama de flujo funcional

Ahora primero dibujemos un diagrama de flujo basado en la función.

registro

imagen

Como se muestra en la imagen de arriba:

  1. Primero se debe iniciar el servidor para monitorear las conexiones del cliente;

  2. Cuando se inicia el cliente, primero se conecta al servidor y muestra la interfaz de inicio de sesión y registro;

  3. Una vez que el servidor recibe la conexión del cliente, creará un hilo secundario dedicado a la comunicación del cliente;

  4. Después de seleccionar el registro, se le solicitará que ingrese el nombre de usuario y la contraseña, encapsule la información de registro en la variable de estructura msg y envíe la señalización al servidor;

  5. Una vez que el servidor recibe la información de registro del cliente, ingresa al proceso de procesamiento de registro;

  6. Función de registro: primero verifique si el nombre de usuario existe, la posición registrada en la matriz en línea [], el valor del indicador es 1, de lo contrario es -1; si el nombre de usuario ha sido registrado, se devolverá el mensaje de error NAME_EXIST; si el nombre de usuario no ha sido registrado, luego busque una ubicación libre, guarde el nombre de usuario y la contraseña en la base de datos en línea [] y regrese la señalización de registro exitoso;

  7. Después de que el cliente reciba las instrucciones de procesamiento de registro del servidor, imprimirá un mensaje y mostrará el menú del paso 2.

Acceso

imagen

Insertar descripción de la imagen aquí

  1. Primero se debe iniciar el servidor para monitorear las conexiones del cliente;

  2. Cuando se inicia el cliente, primero se conecta al servidor y muestra la interfaz de inicio de sesión y registro;

  3. Una vez que el servidor recibe la conexión del cliente, creará un hilo secundario dedicado a la comunicación del cliente;

  4. Después de seleccionar iniciar sesión, se le solicitará que ingrese el nombre de usuario y la contraseña, encapsule la información de inicio de sesión en la variable de estructura msg y envíe la señalización al servidor;

  5. Después de recibir la información de registro del cliente, el servidor ingresa al proceso de inicio de sesión;

  6. Función de inicio de sesión: primero, verifique si el nombre de usuario y la contraseña coinciden en la matriz en línea [], busque y devuelva el subíndice correspondiente, guarde el socket conectado al cliente en la entrada correspondiente y devuelva la información de inicio de sesión exitosa al cliente; si no se encuentra, se devuelve -1 y se devuelve el mensaje de error 0X80000004 al cliente;

  7. Después de que el cliente reciba las instrucciones de procesamiento de registro del servidor, imprimirá un mensaje y establecerá el indicador en línea login_f del cliente en 1. En este momento, se mostrará el menú correspondiente a la función de chat.

código

chat.h

#ifndef _TCP_CHAT
#define _TCP_CHAT

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
 #include <string.h>
 
#define SERVER_PORT 8888
//在线用户 
struct ONLINE{
 int fd;  //-1   
 int flage; //registed or not
 char name[32];
 char passwd[32];
}; 
#define MAX_USER_NUM 64

//C/S通信的结构体
struct protocol{
 int cmd;
 int state;
 char name[32];
 char data[64];
};
 /*cmd*/
#define BROADCAST 0X00000001
#define PRIVATE 0X00000002
#define REGISTE 0X00000004
#define LOGIN 0X00000008
#define ONLINEUSER     0X00000010
#define LOGOUT     0X00000020

/*return code*/
#define OP_OK    0X80000000
#define ONLINEUSER_OK    0X80000001
#define ONLINEUSER_OVER  0X80000002
#define NAME_EXIST 0X80000003
#define NAME_PWD_NMATCH 0X80000004
#define USER_LOGED 0X80000005
#define USER_NOT_REGIST 0X80000006
#endif

cliente.c

/*********************************************
         
*********************************************/
#include "chat.h"

int sockfd;
int addrlen;
struct sockaddr_in   server_addr; 

pthread_t pid;

int login_f =  -1;
 
void *func(void *arg)
{
 int len;
 char buf[64]={0};
 
 while(1)
 {
  if(login_f != 1)
  {
   continue;
  } 
 
  len = read(sockfd,buf,sizeof(buf));
  if(len<=0)
  {
   close(sockfd);
   return;
  }
  buf[len]='\0';
  
  printf("%s\n",buf);  
 } 
}
void broadcast(int fd)
{

}
void private(int fd)
{

}
void list_online_user(sockfd)
{

}
int  registe(int fd)
{
 struct protocol msg,msgback;

 msg.cmd = REGISTE; 
 printf("input your name\n"); 
 scanf("%s",msg.name);
 printf("input your passwd\n"); 
 scanf("%s",msg.data);

 write(sockfd,&msg,sizeof(msg));
 read(sockfd,&msgback,sizeof(msgback));
 if(msgback.state != OP_OK)
 {
  printf("Name had exist,try again!\n"); 
  getchar();
  getchar();
  return -1;
 }else{
  printf("Regist success!\n");
  getchar();
  getchar();
  return 0  ;
 }
}
int login(int fd)
{
 struct protocol msg,msgback;

 msg.cmd = LOGIN; 
 printf("input your name\n"); 
 scanf("%s",msg.name);
 printf("input your passwd\n"); 
 scanf("%s",msg.data);

 write(sockfd,&msg,sizeof(msg));
 read(sockfd,&msgback,sizeof(msgback));
 if(msgback.state != OP_OK)
 {
  printf("Name had exist,try again!\n");
  getchar();
  getchar();
  login_f = -1;
  return NAME_PWD_NMATCH;
 }else{
  printf("Login success!\n");
  getchar();
  getchar();
  login_f = 1;
  return OP_OK  ;
 }
}
int logout(int fd)
{
 close(fd);
 login_f = -1;
}
int main(int argc, char **argv)
{
 int    sel;
 int ret; 
 int min_sel,max_sel;
 int portnumber;
 
 struct protocol msg;
 
 
 if(argc<3)
 {
  printf("cmd: %s ip portnumber\n",argv[0]);
  return;
 }
 //argv2 存放的是端口号 ,读取该端口,转换成整型变量
 if((portnumber=atoi(argv[2]))<0)
 {
  fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
  exit(1);
 }
 sockfd = socket(PF_INET,SOCK_STREAM,0); 
 if(sockfd<0)
 {
  perror("socket() fail\n");
  return;
 }
 
 server_addr.sin_family =  PF_INET; 
 server_addr.sin_port   =  htons(portnumber);
 server_addr.sin_addr.s_addr   =  inet_addr(argv[1]);
 
 addrlen = sizeof(struct sockaddr_in);
 
 connect(sockfd,(struct sockaddr* )&server_addr,addrlen);
 pthread_create(&pid, NULL,func, NULL);  
 while(1)
 {
  //getchar();
  system("clear");
  if(login_f == -1){
   printf("\t 1 注册\n");
   printf("\t 2 登录\n");
  }else if(login_f == 1){
   printf("\t 3 公聊\n");
   printf("\t 4 私聊\n");
   printf("\t 5 在线列表\n");      
  } 
  printf("\t 0 退出\n");
  
  
  fflush(stdin);
  scanf("%d",&sel);
  if(sel == 0)
  {
   break;
  }
  if(login_f == 1)
  {
   min_sel = 3;
   max_sel = 5;
  }else if(login_f == -1){
   min_sel = 1;
   max_sel = 2;
  }
  
  if(sel<min_sel || sel > max_sel)
  {
   printf("Valid choice ,try again\n");
   continue;
  }
  switch(sel)
  {
   case 1:
    registe(sockfd);
    break;
   case 2:
    ret = login(sockfd);
    break;
   case 3:
    broadcast(sockfd);
    break;
   case 4:
    private(sockfd);
    break;
   case 5:
    list_online_user(sockfd); 
   case 0:
    logout(sockfd);
    break;
   default:
    break;
  }
  if(sel == 0)
  {
   exit(0);
  }
 }
}

servidor.c

/*********************************************
          
*********************************************/
#include "chat.h"

struct ONLINE online[MAX_USER_NUM];


void del_user_online(int index)
{
 int i;
 char buf[128]={0};

 if(index <0)
 {
  return;
 }
 online[index].fd = -1;
 sprintf(buf,"%s offline\n",online[index].name);
 //通知所有客户端,某个用户下线了
 for(i=0;i<MAX_USER_NUM;i++)
 {
  if(online[i].fd == -1)
  {
   continue;
  }
  write(online[i].fd,buf,strlen(buf)); 
 } 
 
 
 return;
}
int add_user(int sockfd,struct protocol*msg)
{
 int i,index = -1;
 char buf[128]={0};
 
 for(i=0;i<64;i++)//添加到在线用户列表
 {
  if(online[i].flage == -1)
  {
   online[i].flage= 1;
   strcpy(online[i].name,msg->name);
   strcpy(online[i].passwd,msg->data);
   printf("regist %s to %d \n",msg->name,i);
   index = i;
   return index;
  }  
 }
 return index;
}
void broadcast(int index,struct protocol*msg)
{

}
int find_dest_user_online(int sockfd,int *index,struct protocol*msg)
{
 int i;
 
 for(i=0;i<MAX_USER_NUM;i++)
 {
 //this pos not use
  if(online[i].flage== -1)
  {
   continue;   
  }
  
  if((strcmp(msg->name,online[i].name)==0)&&(strcmp(msg->data,online[i].passwd)==0))
  {
   if(online[i].fd == -1)
   {
    online[i].fd = sockfd;
    *index = i ;
    return OP_OK;
   }else{
    //user had loged
    printf("%s had login\n",online[i].name);
    return USER_LOGED;
   }
     
  }
 }
 return NAME_PWD_NMATCH;
}
int find_dest_user(char *name)
{
 int i;
 
 for(i=0;i<MAX_USER_NUM;i++)
 {
 
  if(online[i].flage == -1)
  {
   continue;   
  }
  
  if(strcmp(name,online[i].name)==0)
  {
   return i;   
  }
 }
 return -1;
}

void private(int index,struct protocol*msg)
{

}
void list_online_user(int index)
{

}

void registe(int sockfd,int *index,struct protocol*msg)
{
 int dest_index;
 char buf[128];
 struct protocol msg_back;

 msg_back.cmd = REGISTE; 
 //找到那个人
 dest_index = find_dest_user(msg->name);

 if(dest_index == -1)
 { // this user can registe
  *index = add_user(sockfd,msg);
  
  online[*index].flage = 1;
  msg_back.state = OP_OK;
  
  printf("user %s regist success!\n",msg->name);
  write(sockfd,&msg_back,sizeof(msg_back));
  
  return;
 }else{
  msg_back.state = NAME_EXIST;
  printf("user %s exist!\n",msg->name);

  write(sockfd,&msg_back,sizeof(msg_back));
  return;
 } 
}
void login(int sockfd,int *index,struct protocol*msg)
{
 int i;
 int ret;
 char buf[128];
 struct protocol msg_back;

 msg_back.cmd = LOGIN; 
 
 //找到那个人
 ret = find_dest_user_online(sockfd,index,msg);
 
 if(ret != OP_OK)
 {
  msg_back.state = ret;
  strcpy(buf,"there is no this user\n");
  printf("user %s login fail!\n",msg->name);
  
  write(sockfd,&msg_back,sizeof(msg_back));
  return;
 }else{
  msg_back.state = OP_OK;
  strcpy(msg_back.data,"login success\n");
  printf("user %s login success!index =%d \n",msg->name,*index);
  write(online[*index].fd,&msg_back,sizeof(msg_back));
 }
 //通知所有客户端,某个用户上线了
 sprintf(buf,"%s online\n",online[*index].name);
 
 for(i=0;i<MAX_USER_NUM;i++)
 {
  if(online[i].fd != -1)
  {
   write(online[i].fd,buf,strlen(buf));
  }   
 }
 
}
void *func(void *arg)
{
 int sockfd = *((int*)arg);
 char buf[64];
 int len;
 int index = -1;//该用户在在线用户列表的下标
 struct protocol msg;
 
 free(arg); 

 //进入聊天了
 while(1)
 {
  len = read(sockfd,&msg,sizeof(msg));
  if(len<=0)
  {//下线
   printf("%s offline\n",online[index].name);
   //从在线列表中删除
   del_user_online(index);
   close(sockfd);
   return;
  }
  
  switch(msg.cmd)
  {
   case REGISTE:
    registe(sockfd,&index,&msg);
    break;
   case LOGIN:
    login(sockfd,&index,&msg);
    break;
   case BROADCAST:
    broadcast(index,&msg);
    break;
   case PRIVATE:
    private(index,&msg);
    break;
   case ONLINEUSER:
    list_online_user(index);
    break;
   default:
    break;
  }
  
 } 
}

int main(int argc, char **argv)
{
 int lsfd,newfd;
 int addrLen,cliaddrlen;
 struct sockaddr_in   my_addr; 
 struct sockaddr_in   cli_adr; 
 char buf[64]="xuezhiqian fuhele\n";
 pthread_t pid;
 int *arg;
 int i;
 int portnumber;
 
 if(argc<2)
 {
  printf("cmd: %s  portnumber\n",argv[0]);
  return;
 }
/*¶˿ںŲ»¶ԣ¬͋³ö*/
 if((portnumber=atoi(argv[1]))<0)
 {
  fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
  exit(1);
 } 
 lsfd = socket(PF_INET,SOCK_STREAM,0); 
 if(lsfd<0)
 {
  perror("socket() fail\n");
  return;
 }
 bzero(&my_addr,sizeof(struct sockaddr_in));
 my_addr.sin_family =  PF_INET; 
 my_addr.sin_port   =  htons(portnumber);
 my_addr.sin_addr.s_addr   =  htonl(INADDR_ANY);
 addrLen = sizeof(struct sockaddr_in);
 
 if(bind(lsfd,(struct sockaddr* )&my_addr ,addrLen)<0)
 {
  perror("bind() fail\n");
  return;  
 }
 
 listen(lsfd,5);
 cliaddrlen = sizeof(struct sockaddr_in);
 
 for(i=0;i<64;i++)
 {
  online[i].fd = -1;
  online[i].flage= -1;
 }
 while(1)
 {
  newfd = accept(lsfd,(struct sockaddr *)&cli_adr,&cliaddrlen);
  printf("client:ip:%s   port:%d  \n",
   inet_ntoa(cli_adr.sin_addr),cli_adr.sin_port);
    
  arg = malloc(sizeof(int));
  *arg = newfd;//必须搞清楚为什么要申请内存
  
         pthread_create(&pid,NULL,func, (void*)arg); 
 }
 close(newfd);
 close(lsfd);
}

captura de pantalla

Registro cliente 1

Nombre de usuario: yikoulinux Contraseña: registro del cliente qqqq

imagen

Registro del servidor

imagen

Registro cliente 2

Nombre de usuario: yikoupeng Contraseña: registro de servidor/cliente qqqq

imagen

Inicio de sesión del cliente 1

Iniciar sesión

imagen

Presione Entrar, el cliente ocultará los menús de inicio de sesión y registro, y mostrará los menús para el chat público, el chat privado y la lista en línea. Como se muestra abajo:

imagen

Insertar descripción de la imagen aquí

Inicio de sesión del cliente 2

Iniciar sesión

imagen

Observación:

  1. Este artículo solo presenta la implementación de la función de inicio de sesión y registro;

  2. Debido a que este artículo solo analiza la implementación de funciones, muchas operaciones anormales y propensas a errores no se completan por completo;

  3. La información del usuario en línea debe guardarse en una base de datos [como sqlite] Para facilitar la comprensión de los lectores, este artículo utilizará temporalmente una matriz;

  4. El registro y el inicio de sesión no implementan verificación secundaria ni ingreso implícito de contraseñas.

Supongo que te gusta

Origin blog.csdn.net/weixin_41114301/article/details/133384566
Recomendado
Clasificación