## 添加用户注册功能 #### 要求 在之前实验的服务器客户端代码中增添用户注册的功能,在客户端为用户提供注册的选项,并将用户注册的账号、密码发送到服务器端,并保存在服务器的 MySQL 数据库中。 1. 在服务器本地 Mysql 中创建新数据库 ChatProject,库中有一张表叫 USER,该表中有账号 NAME 和密码 PASSWORD 两项属性。 2. 为客户端添加注册功能,让用户注册账号密码,客户端将注册信息发送到服务器端,注意:当用户注册时应输入两次密码,如果密码不一致需要重新输入。 3. 服务器端接收来自客户端的注册信息,将用户的账号、密码写入本地 Mysql 数据库。 4. 要面向对象编程,进行类封装。 #### 具体实现 首先在 MySQL 控制台创建数据库 ChatProject,如下: ```sql create database ChatProject; ``` ![图片描述](https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202304081434336.png) 接下来先用 use 命令切换到新建的数据库。 ```sql use ChatProject; ``` 然后新建一张表格叫 USER 用来保存账号信息,表中有账号 NAME 和密码 PASSWORD 两项属性,都为 VARCHAR 可变长度字符串类型,且将账号 NAME 设为 PRIMARY KEY 主键,主键不允许重复保证了账号的唯一性,而且主键能自动建立索引加快查询速度。 ```sql CREATE TABLE USER( NAME VARCHAR(20) PRIMARY KEY, PASSWORD VARCHAR(20) ); ``` ![图片描述](https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202304081434324.png) 建好表之后我们可以查看当前数据库中所有的表格。 ```sql show tables; ``` ![图片描述](https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202304081434320.jpeg) 最后输入 `exit` 即可离开 MySQL 控制台回到终端。 创建好了表格之后,我们就可以开始为 Client 类和 Server 类添加注册账号的代码了。 首先修改 `client.h` 头文件,添加一个函数 HandleClient,该函数将在与服务器连接建立之后开始工作,与用户进行交互并处理各项事务。 ```cpp #ifndef CLIENT_H #define CLIENT_H #include "global.h" class client{ private: int server_port; string server_ip; int sock; public: client(int port,string ip); ~client(); void run(); static void SendMsg(int conn); static void RecvMsg(int conn); void HandleClient(int conn); }; #endif ``` 然后修改 `client.cpp` 文件中的 run 函数,连接完服务器之后就调用 HandleClient,并将文件描述符作为参数传入,如下: ```cpp void client::run(){ //定义sockfd sock = socket(AF_INET,SOCK_STREAM, 0); //定义sockaddr_in struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(server_port); //服务器端口 servaddr.sin_addr.s_addr = inet_addr(server_ip.c_str()); //服务器ip //连接服务器,成功返回0,错误返回-1 if (connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("connect"); exit(1); } cout<<"连接服务器成功\n"; HandleClient(sock); return; } ``` 接下来设计 `client.cpp` 文件中的 HandleClient 函数,开头先打印一段信息指示用户操作,然后开始处理事务,如果用户输入 2 就进入注册模块,注册时输入两次密码,密码不一致就要重新输入。最后将注册的账号和密码格式化成“name:xxxpass:yyy”(xxx 代表用户名,yyy 代表密码)发送到服务器端,格式化之后可以方便服务器提取出用户名和密码。 ```cpp void client::HandleClient(int conn){ int choice; string name,pass,pass1; //bool if_login=false;//记录是否登录成功 cout<<" ------------------\n"; cout<<"| |\n"; cout<<"| 请输入你要的选项:|\n"; cout<<"| 0:退出 |\n"; cout<<"| 1:登录 |\n"; cout<<"| 2:注册 |\n"; cout<<"| |\n"; cout<<" ------------------ \n\n"; //开始处理各种事务 while(1){ //if(if_login) // break; cin>>choice; if(choice==0) break; //注册 else if(choice==2){ cout<<"注册的用户名:"; cin>>name; while(1){ cout<<"密码:"; cin>>pass; cout<<"确认密码:"; cin>>pass1; if(pass==pass1) break; else cout<<"两次密码不一致!\n\n"; } name="name:"+name; pass="pass:"+pass; string str=name+pass; send(conn,str.c_str(),str.length(),0); cout<<"注册成功!\n"; cout<<"\n继续输入你要的选项:"; } } } ``` 接下来开始设计服务器端,因为服务器要连接 MySQL,所以我们先在 `global.h` 中添加 mysql 头文件,如下: ```cpp #ifndef _GLOBAL_H #define _GLOBAL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include //新添加 using namespace std; #endif ``` 然后编写 `server.h`,添加静态函数 HandleRequest 的定义。 server.h: ```cpp #ifndef SERVER_H #define SERVER_H #include "global.h" class server{ private: int server_port; int server_sockfd; string server_ip; static vector sock_arr; public: server(int port,string ip); ~server(); void run(); static void RecvMsg(int conn); static void HandleRequest(int conn,string str); }; #endif ``` 修改 `server.cpp` 中 RecvMsg 的代码,让其接收到信息之后调用 HandleRequest 函数,并传入文件描述符和接收到的字符串作为参数。 ```cpp void server::RecvMsg(int conn){ //接收缓冲区 char buffer[1000]; //不断接收数据 while(1) { memset(buffer,0,sizeof(buffer)); int len = recv(conn, buffer, sizeof(buffer),0); //客户端发送exit或者异常结束时,退出 if(strcmp(buffer,"exit")==0 || len<=0){ close(conn); sock_arr[conn]=false; break; } cout<<"收到套接字描述符为"< use ChatProject; > select * from USER; ``` ![图片描述](https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202304081434326.png) 可见成功注册。 最后提交 git: ![图片描述](https://typoraflykhan.oss-cn-beijing.aliyuncs.com/202304081434410.png)