基于Linux 系统聊天室登录与注册实现(03)

简介: 基于Linux 系统聊天室登录与注册实现(03)

上一篇我们已经讲了如何搭建一个多线程的服务器模型,可以支持多个客户端同时连接服务器,本篇我们来实现多个客户端,如何实现向服务器注册信息,并实现登录的功能。

数据结构

接着上一篇的实例代码继续增加功能。要实现注册和登录功能,那么我们就必须要让服务器和客户端在交互数据包的时候按照统一的格式收发信令。信令格式

//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    //退出

在线用户信息服务器需要维护所有用户信息,需要知道用户是否在线,是否注册。

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

注册的客户端信息需要存储在服务器,为了简单起见,我们暂时不用数据库存储,只定义一个全局的数组保存客户端信息,并且规定只允许64个客户端登录。

服务器处理结果返回值

/*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  //登录时,提示用户没有注册

功能流程图

现在我们根据功能,首先画一个流程图。

注册

由上图所示:

  1. 服务器要先启动,监听客户端的连接;
  2. 客户端启动,首先连接服务器,并显示登陆、注册界面;
  3. 服务器接收到客户端连接后,会创建一个子线程专门用于于客户端的通信;
  4. 选择注册后,提示输入用户名、密码,封装注册信息到结构体变量msg中,并发送该信令给服务器;
  5. 服务器接收到客户端注册信息后,进入注册处理流程;
  6. 注册功能:首先查找该用户名是否存在,数组online[]中注册的位置,flage值为1,否则为-1;如果该用户名已经注册,则返回NAME_EXIST 错误信息;如果该用户名没有被注册,则找一个空闲位置,将该用户名密码保存到数据库online[]中,并返回注册成功的信令;
  7. 客户端接收到服务器注册处理指令后,会打印提示信息,并显示步骤2的菜单。

登录

在这里插入图片描述

  1. 服务器要先启动,监听客户端的连接;
  2. 客户端启动,首先连接服务器,并显示登陆、注册界面;
  3. 服务器接收到客户端连接后,会创建一个子线程专门用于于客户端的通信;
  4. 选择登陆后,提示输入用户名、密码,封装登陆信息到结构体变量msg中,并发送该信令给服务器;
  5. 服务器接收到客户端注册信息后,进入登陆处理流程;
  6. 登陆功能:首先查找该用户名、密码是否在数组online[]中存在匹配项,找到返回对应的下标,并将于该客户端相连接的套接字保存到对应的条目中,返回登陆成功信息给客户端;如果没有找到,则返回-1,并返回0X80000004错误信息给客户端;
  7. 客户端接收到服务器注册处理指令后,会打印提示信息,并设置客户端在线的标记login_f 为1,此时会显示 聊天功能对应的菜单。

代码

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

client.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);
  }
 }
}

server.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);
}

截图

客户端1注册

用户名:yikoulinux 密   码:qqqq客户端log

服务器log

客户端2注册

用户名:yikoupeng 密   码:qqqq服务器/客户端log

客户端1登录

登录log

按下回车,客户端会隐藏登录、注册的菜单,并显示公聊、私聊、在线列表的菜单。如下图所示:

在这里插入图片描述

客户端2登录

登录log

备注:

  1. 本篇只介绍登陆注册功能的实现;
  2. 因为本文只讨论功能的实现,对于很多异常出错的操作并没有全部完善;
  3. 在线用户的信息应该保存到数据库中【比如sqlite】,本篇为了便于读者理解,暂时用数组替代;
  4. 注册登录没有实现密码的二次校验和隐式输入。
目录
相关文章
|
1月前
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
206 78
|
10天前
|
缓存 安全 Linux
Linux系统查看操作系统版本信息、CPU信息、模块信息
在Linux系统中,常用命令可帮助用户查看操作系统版本、CPU信息和模块信息
66 23
|
1月前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
77 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
1月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
109 13
|
2月前
|
Ubuntu Linux 网络安全
linux系统ubuntu中在命令行中打开图形界面的文件夹
在Ubuntu系统中,通过命令行打开图形界面的文件夹是一个高效且实用的操作。无论是使用Nautilus、Dolphin还是Thunar,都可以根据具体桌面环境选择合适的文件管理器。通过上述命令和方法,可以简化日常工作,提高效率。同时,解决权限问题和图形界面问题也能确保操作的顺利进行。掌握这些技巧,可以使Linux操作更加便捷和灵活。
71 3
|
1月前
|
Ubuntu Linux C++
Win10系统上直接使用linux子系统教程(仅需五步!超简单,快速上手)
本文介绍了如何在Windows 10上安装并使用Linux子系统。首先,通过应用商店安装Windows Terminal和Linux系统(如Ubuntu)。接着,在控制面板中启用“适用于Linux的Windows子系统”并重启电脑。最后,在Windows Terminal中选择安装的Linux系统即可开始使用。文中还提供了注意事项和进一步配置的链接。
48 0
|
1月前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。
|
SQL Linux 数据库
基于LINUX的多功能聊天室
原文:基于LINUX的多功能聊天室 基于LINUX的多功能聊天室 其实这个项目在我电脑已经躺了多时,最初写完项目规划后,我就认认真真地去实现了它,后来拿着这个项目区参加了面试,同样面试官也拿这个项目来问我,当然我是做过一遍了,而且为了面试,我将什么strcpy,strlen等最常用的函数都自己实现...
1110 0
|
2月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
258 8
|
2月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
987 6