博客网址:www.shicoder.top
微信:kj11011029
欢迎加群聊天 :452380935
本次主要是对Chatserver
和Chatservice
进行代码实现
ChatServer
首先我们利用muduo
库建立一个ChatServer
类,muduo
库的讲解我会在后面再写一个项目来深入分析
class ChatServer
{
public:
ChatServer(EventLoop *loop,
const InetAddress &listenAddr,
const string &nameArg);
void start();
private:
TcpServer _server;
EventLoop *_loop;
// 回调连接相关的事件
void onConnection(const TcpConnectionPtr &conn);
// 回调读写事件
void onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time);
};
其中主要的代码注释已经写出来了,注意由于muduo
库已经帮我们实现了套接字的连接,我们只需要自己定义回调函数,即可让客户端连接时候以及客户端发送请求的时候进行自我回调。
注意为什么要在ChatServer
类中实现上述代码的构造函数
我们去muduo
中的TcpServer
源码可知,TcpServer
没有空的构造函数,所以我们必须要用初始化列表对TcpServer
进行初始化。下面是TcpServer
源码的构造函数
//TcpServer(EventLoop* loop, const InetAddress& listenAddr);
TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option = kNoReusePort);
~TcpServer(); // force out-line dtor, for std::unique_ptr members.
以下代码是ChatServer
对应的实现细节
ChatServer::ChatServer(EventLoop *loop,
const InetAddress &listenAddr,
const string &nameArg)
: _server(loop, listenAddr, nameArg), _loop(loop)
{
_server.setConnectionCallback(bind(&ChatServer::onConnection, this, _1));
_server.setMessageCallback(bind(&ChatServer::onMessage, this, _1, _2, _3));
_server.setThreadNum(4);
}
void ChatServer::start()
{
_server.start();
}
void ChatServer::onConnection(const TcpConnectionPtr &conn)
{
// 用户断开连接
if (!conn->connected())
{
conn->shutdown();
}
}
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{
string buf = buffer->retrieveAllAsString();
json js = json::parse(buf);
// 通过js中的id来获取一个handler,这样把网络模块和业务模块分开
// 转换成int类型
auto handler = ChatService::instance()->getHandler(js["msgid"].get<int>());
// handler在执行的时候才知道是login还是reg
handler(conn, js, time);
}
注意在onMessage
方法中,我们为了把网络模块和业务模块分开,采用的是回调机制,将msgid
和消息对应的处理函数绑定起来,然后当一个消息接受到之后,利用json
库对msgid
进行解析,从而自动回调对应的处理函数。
ChatService
该类主要就是针对msgid
和对应的handler
进行绑定。
class ChatService
{
public:
// 获取单例
static ChatService *instance();
// 处理登录业务
void login(const TcpConnectionPtr &conn, json &js, Timestamp);
// 处理注册业务
void reg(const TcpConnectionPtr &conn, json &js, Timestamp);
// 获取消息对应的处理器
MsgHandler getHandler(int msgid);
private:
ChatService();
// 存储消息id和业务处理的方法
unordered_map<int, MsgHandler> _msgHandlerMap;
};
为了进行绑定操作,我们在ChatService
构造函数直接将msgid
和对应handler
进行绑定
// 注册消息以及对应的回调操作
ChatService::ChatService()
{
_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
_msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
}
其中std::bind
的语法大家可以自行百度,其实可以理解为一种函数指针,将LOGIN_MSG
和ChatService::login
绑定起来,当收到LOGIN_MSG
,自动去调用ChatService::login
方法。然后我们主要是实现登陆和注册两个函数的具体代码。
数据库建立
为了实现登陆和注册功能,我们需要自行建立对应的数据库,我们这里使用的MySQL
数据库。对应的user
表如下:
字段名称 | 字段类型 | 字段说明 | 约束 |
---|---|---|---|
id | INT | 用户id | PRIMARY KEY、AUTO_INCREMENT |
name | VARCHAR(50) | 用户名 | NOT NULL, UNIQUE |
password | VARCHAR(50) | 用户密码 | NOT NULL |
state | ENUM('online', 'offline') | 当前登录状态 | DEFAULT 'offline' |
然后对登陆和注册函数进行实现
// 处理登录业务
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp)
{
int id = js["id"].get<int>();
string password = js["password"];
User user = _usermodel.query(id);
if (user.getId() == id && user.getPwd() == password)
{
if (user.getState() == "online")
{
// 用户已经登陆,不允许重复登陆
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["error"] = 2; // 2表示已经登陆
response["errormsg"] = "已经登陆,不允许重复登陆";
conn->send(response.dump());
}
else
{
//登陆成功,然后更新用户登陆信息
user.setState("online");
_usermodel.updateState(user);
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["error"] = 0; //0表示成功
response["id"] = user.getId();
response["name"] = user.getName();
conn->send(response.dump());
}
}
else
{
// 该用户不存在
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["error"] = 1; //1表示不存在
response["errormsg"] = "用户名或密码不存在";
conn->send(response.dump());
}
}
// 处理注册业务
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp)
{
string name = js["name"];
string password = js["password"];
User user;
user.setName(name);
user.setPwd(password);
bool ans = _usermodel.insert(user);
if (ans)
{
// 注册成功
json response;
response["msgid"] = REG_MSG_ACK;
response["error"] = 0; //0表示成功
response["id"] = user.getId();
conn->send(response.dump());
}
else
{
// 注册失败
json response;
response["msgid"] = REG_MSG_ACK;
response["error"] = 1; //1表示失败
conn->send(response.dump());
}
}
注意我们同样在这里使用低耦合思想,把业务和数据库模块分开,注意_usermodel.query(id)
这一句,其实query
就是其中数据库的操作模块,里面才是具体的数据库语句。
这样建立了ChatServer
后,就可以在main
函数中直接启动
EventLoop loop;
InetAddress addr("127.0.0.1", 6000);
ChatServer server(&loop, addr, "ChatServer");
server.start();
loop.loop();
评论 (0)