通信
5.服务器-注册功能
1.类型描述,数据采用JSON格式
步骤:
数据:
2.Ubuntu安装jsoncpp
sudo apt-get install libjsoncpp-dev
ls /usr/include/jsoncpp/json/
检查是否安装成功- 使用的时候包含头文件
#include <jsoncpp/json/json.h>
- 第三步编译的时候链接库
g++ -ljsoncpp
3.数据库写数据
每个用户都是一个表,表名就是用户名,里面存放的是password和friend
create table 肥猪 (password varchar(16), friend varchar(4096));
show tables like '肥猪';
insert into 肥猪 (password) values("000000");
4.数据库检索用户是否存在和添加用户
chat_databases.h .cpp
#.h
class ChatDataBases
{
bool my_database_user_exist(const std::string name); //注册时判断用户是否存在
void my_database_add_user(const std::string name, const std::string password); //注册添加用户
};
**********************************************************************
#.cpp
bool ChatDataBases::my_database_user_exist(const std::string name)
{
//先设置中文
if (mysql_query(mysql,"set names utf8") != 0)
{
std::cout << "mysql_query error" << std::endl;
}
//封装sql语句
char sql[128] = {0};
sprintf(sql,"show tables like '%s';",name.c_str()); //sprintf接收的是char *
if (mysql_query(mysql,sql) != 0 )
{
std::cout << "mysql_query error " << std::endl;
}
//查询结果
MYSQL_RES *res = mysql_store_result(mysql);
MYSQL_ROW row = mysql_fetch_row(res);
if (row) //不为空 存在该用户
{
return true;
}
else //为空 不存在该用户
{
return false;
}
}
void ChatDataBases::my_database_add_user(const std::string name, const std::string password)
{
//创建表
char sql[128] = {0};
sprintf(sql,"create table %s (password varchar(16), friend varchar(4096));",name.c_str()); //sprintf接收的是char *
if (mysql_query(mysql,sql) != 0 )
{
std::cout << "mysql_query error" << std::endl;
}
//往表插入数据,这里没有好友字段,因为刚注册是不会有好友的,直接写password就行
memset(sql, 0 , sizeof(sql));
sprintf(sql, "insert into %s (password) values('%s');",name.c_str(), password.c_str());
if (mysql_query(mysql,sql) != 0 )
{
std::cout << "mysql_query error" << std::endl;
}
}
5.服务器根据数据库检查结果返回数据给客户端
server.h .cpp
#include "chatlist.h"
#include "chat_databases.h"
class Server
{
private:
static ChatDataBases *mydatabase; //数据库对象
static void server_register(struct bufferevent *bev, const Json::Value val);
};
********************************************************************
#.cpp 这里有修改,之前的代码有内存泄露问题 base 和 bev 的释放时机不对
//线程函数一般没有返回值
void Server::client_handle(int fd)
{
//创建集合,这个是工作集合
struct event_base * base = event_base_new();
//创建bufferevent对象,并与base绑定
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (bev == NULL)
{
printf("bufferevent_socket_new error \n");
}
//给bufferevent设置回调函数
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
//使能回调函数
bufferevent_enable(bev,EV_READ);
//监听
event_base_dispatch(base);
//释放,不放这里,因为只有当客户端断开连接或者出现异常时才释放,在event_cb中写
//event_base_free(base);
//bufferevent_free(bev);
}
//读取数据,客户端发送过来的数据,注册,登录那些都在这里写
void Server::read_cb(struct bufferevent *bev, void *ctx)
{
char buf[1024] = {0};
int size = bufferevent_read(bev, buf, sizeof(buf));
if (size < 0)
{
std::cout << "bufferevent_read error \n" << std::endl;
}
else
{
std::cout << "传过来的数据:" << buf << std::endl;
}
/*
数据的传送采用josn的格式,后面下面进行解析数据,然后
进行实现不同的功能,注册登录那些
*/
Json::Reader reader; //解析json对象
Json::FastWriter writer; //封装json对象
Json::Value val; //json对象
if (!reader.parse(buf, val)) //把字符串转换成json对象
{
std::cout << "服务器解析数据失败!Json::Reader parse error "<<std::endl;
}
std::string cmd = val["cmd"].asString(); //通过键解析键值
if (cmd == "register") //注册功能
{
server_register(bev, val); //要往bev写入数据,val是上面buf转换成json的数据
}
else
{
}
}
//在异常情况下或者客户端断开连接时,会触发event_cb函数调用
void Server::event_cb(struct bufferevent *bev, short what, void *ctx)
{
// 释放bufferevent对象
bufferevent_free(bev);
// 释放event_base对象
struct event_base *base = bufferevent_get_base(bev);
event_base_loopbreak(base);
event_base_free(base);
}
void Server::server_register(struct bufferevent *bev, const Json::Value val)
{
//先连接数据库,记得断开
mydatabase->my_database_connect("user");
//检查数据库是否有这个用户,这里在数据库操作里面写就好,只需要知道结果就行
if (mydatabase->my_database_user_exist(val["user"].asString())) //用户存在,返回客户端注册失败
{
//返回的JSON格式数据
Json::Value val;
val["cmd"] = "register_reply";
val["result"] = "failure";
//返回给客户端,用bev
// const char* data= Json::FastWriter().write(val).c_str(); //val在这个过程中被释放了,data是空值
std::string data = Json::FastWriter().write(val);
if(bufferevent_write(bev, data.c_str(), strlen(data.c_str())) < 0)
{
std::cout << "bufferevent_write error" << std::endl;
}
}
else //用户不存在,不存在就写入数据库当中,返回客服端完成注册
{
//写入数据库
mydatabase->my_database_add_user(val["user"].asString(), val["password"].asString());
//返回的JSON格式数据
Json::Value val;
val["cmd"] = "register_reply";
val["result"] = "success";
//返回给客户端,用bev
std::string data = Json::FastWriter().write(val);
if(bufferevent_write(bev, data.c_str(), strlen(data.c_str())) < 0)
{
std::cout << "bufferevent_write error" << std::endl;
}
}
//
mydatabase->my_database_disconnect();
}
6.测试
要安装json-c,https://blog.csdn.net/qq_52201535/article/details/133581163
client_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <json-c/json.h>
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket error");
exit(1);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0 , sizeof(server_addr));//情空
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("192.168.24.149");
server_addr.sin_port = htons(8000);
//向服务器发起连接
int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1)
{
perror("connect error");
exit(1);
}
//char buf[320] = "{'cmd':'register','user':'肥猪','password':'001227'};";
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "cmd", json_object_new_string("register"));
json_object_object_add(obj, "user", json_object_new_string("肥猪"));
json_object_object_add(obj, "password", json_object_new_string("001227"));
const char *buf = json_object_to_json_string(obj);
ret = send(sockfd, buf, strlen(buf), 0);
if (ret == -1)
{
perror("send error");
exit(1);
}
char s[128] = {0};
ret = recv(sockfd, s, sizeof(s), 0);
printf("收到服务器回复:%s\n",s);
close(sockfd);
return 0;
}
编译:gcc client_test.c -o client_test -ljson-c
结果:
删除表drop table 肥猪;
后再试一次