flykhan 3c88d33217 | ||
---|---|---|
.. | ||
README.md | ||
client.cpp | ||
client.h | ||
global.h | ||
makefile | ||
server.cpp | ||
server.h | ||
test_client.cpp | ||
test_server.cpp |
README.md
添加用户注册功能
要求
在之前实验的服务器客户端代码中增添用户注册的功能,在客户端为用户提供注册的选项,并将用户注册的账号、密码发送到服务器端,并保存在服务器的 MySQL 数据库中。
- 在服务器本地 Mysql 中创建新数据库 ChatProject,库中有一张表叫 USER,该表中有账号 NAME 和密码 PASSWORD 两项属性。
- 为客户端添加注册功能,让用户注册账号密码,客户端将注册信息发送到服务器端,注意:当用户注册时应输入两次密码,如果密码不一致需要重新输入。
- 服务器端接收来自客户端的注册信息,将用户的账号、密码写入本地 Mysql 数据库。
- 要面向对象编程,进行类封装。
具体实现
首先在 MySQL 控制台创建数据库 ChatProject,如下:
create database ChatProject;
接下来先用 use 命令切换到新建的数据库。
use ChatProject;
然后新建一张表格叫 USER 用来保存账号信息,表中有账号 NAME 和密码 PASSWORD 两项属性,都为 VARCHAR 可变长度字符串类型,且将账号 NAME 设为 PRIMARY KEY 主键,主键不允许重复保证了账号的唯一性,而且主键能自动建立索引加快查询速度。
CREATE TABLE USER(
NAME VARCHAR(20) PRIMARY KEY,
PASSWORD VARCHAR(20)
);
建好表之后我们可以查看当前数据库中所有的表格。
show tables;
最后输入 exit
即可离开 MySQL 控制台回到终端。
创建好了表格之后,我们就可以开始为 Client 类和 Server 类添加注册账号的代码了。
首先修改 client.h
头文件,添加一个函数 HandleClient,该函数将在与服务器连接建立之后开始工作,与用户进行交互并处理各项事务。
#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,并将文件描述符作为参数传入,如下:
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 代表密码)发送到服务器端,格式化之后可以方便服务器提取出用户名和密码。
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 头文件,如下:
#ifndef _GLOBAL_H
#define _GLOBAL_H
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
#include <thread>
#include <vector>
#include <mysql/mysql.h> //新添加
using namespace std;
#endif
然后编写 server.h
,添加静态函数 HandleRequest 的定义。
server.h:
#ifndef SERVER_H
#define SERVER_H
#include "global.h"
class server{
private:
int server_port;
int server_sockfd;
string server_ip;
static vector<bool> 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 函数,并传入文件描述符和接收到的字符串作为参数。
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<<"收到套接字描述符为"<<conn<<"发来的信息:"<<buffer<<endl;
string str(buffer);
HandleRequest(conn,str);
}
}
接下来在 server.cpp
中给出 HandleRequest 具体实现,首先连接 MySQL 数据库,然后判断字符串是否含有“name:”,含有就说明是注册信息,通过字符串的 find、substr 函数提取出用户名和密码,然后组合成 SQL 插入语句并通过 mysql_query 执行该 SQL 语句即可。这里我们插入数据用的 SQL 语句为“INSERT INTO USER VALUES ("xxx","yyy");”(xxx 和 yyy 为具体的用户名、密码)。
void server::HandleRequest(int conn,string str){
char buffer[1000];
string name,pass;
// bool if_login=false;//记录当前服务对象是否成功登录
//string login_name;//记录当前服务对象的名字
//string target_name;//记录发送信息时目标用户的名字
//int group_num;//记录群号
//连接MYSQL数据库
MYSQL *con=mysql_init(NULL);
mysql_real_connect(con,"127.0.0.1","root","","ChatProject",0,NULL,CLIENT_MULTI_STATEMENTS);
if(str.find("name:")!=str.npos){
int p1=str.find("name:"),p2=str.find("pass:");
name=str.substr(p1+5,p2-5);
pass=str.substr(p2+5,str.length()-p2-4);
string search="INSERT INTO USER VALUES (\"";
search+=name;
search+="\",\"";
search+=pass;
search+="\");";
cout<<"sql语句:"<<search<<endl<<endl;
mysql_query(con,search.c_str());
}
}
最后修改 makefile
,因为服务器要连接 MySQL,所以要加上 -l
来动态链接。
all: test_server.cpp server.cpp server.h test_client.cpp client.cpp client.h global.h
g++ -o test_client test_client.cpp client.cpp -lpthread
g++ -o test_server test_server.cpp server.cpp -lpthread -lmysqlclient
test_server: test_server.cpp server.cpp server.h global.h
g++ -o test_server test_server.cpp server.cpp -lpthread -lmysqlclient
test_client: test_client.cpp client.cpp client.h global.h
g++ -o test_client test_client.cpp client.cpp -lpthread
clean:
rm test_server
rm test_client
然后用 make
编译即可,接下来进行测试:
make
./test_server
# 新开一个终端,执行如下命令
./test_client
然后另开一个终端看看数据库中的 USER 表是否已经有用户数据:
mysql -u root
> use ChatProject;
> select * from USER;
可见成功注册。
最后提交 git: