C++项目 | 集群聊天服务器 | 用户登录业务 && 记录用户的连接信息以及线程安全问题 1.query函数 功能:根据用户号码查询用户信息
封装sql的select查询语句
把查询结果封装到User中返回
没查询到就返回默认结果(id==-1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 User UserModel::query (int id) { char sql[1024 ]={0 }; sprintf (sql,"select * from user where id = %d" , id); MySQL mysql; if (mysql.connect ()) { MYSQL_RES *res=mysql.query (sql); if (res!=nullptr ) { MYSQL_ROW row=mysql_fetch_row (res); if (row!=nullptr ) { User user; user.setId (atoi (row[0 ])); user.setName (row[1 ]); user.setPwd (row[2 ]); user.setState (row[3 ]); mysql_free_result (res); return user; } } } return User (); }
2.updateState函数 修改用户在线状态
封装sql语句,用户登录后状态改为在线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 bool UserModel::updateState (User user) { char sql[1024 ]={0 }; sprintf (sql,"update user set state = '%s' where id = %d" , user.getState ().c_str (),user.getId ()); MySQL mysql; if (mysql.connect ()) { if (mysql.update (sql)) { return true ; } } return false ; }
3.login函数 1.json中的id默认是字符串要转为int
2.id==-1表示没有这个用户
3.调用query查询id对应的user
4.在线状态为offline才登录,不然就是登录失败
5.没查到id说明没有这个账户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void ChatService::login (const TcpConnectionPtr& conn,json &js,Timestamp time) { int id=js["id" ].get <int >(); string pwd=js["password" ]; User user=_userModel.query (id); if (user.getId ()==id && user.getPwd ()==pwd) { if (user.getState ()=="online" ) { json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=2 ; response["errmsg" ]="该用户已经登录,请重新输入新账号" ; conn->send (response.dump ()); } else { user.setState ("online" ); _userModel.updateState (user); json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=0 ; response["id" ]=user.getId (); response["name" ]=user.getName (); conn->send (response.dump ()); } } else { json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=1 ; response["errmsg" ]="用户名或者密码错误" ; conn->send (response.dump ()); } }
4.测试 1 2 3 4 5 6 7 {"msgid" :1 ,"id" :1 ,"password" :"123456" } {"msgid" :1 ,"id" :2 ,"password" :"123456" } update user set state = 'offline' where id = 1
用户已经登录
用户名或密码错误
登录成功
5.记录用户的连接信息以及线程安全问题 为什么要记录用户的连接信息?
一个用户登录后服务器要记录用户的连接信息
如果A和B用户发送消息,而服务器不知道A用户的连接的话就无法确切的把B的消息发给A
所以chatServer是一个长连接的服务器,只要用户登录着,那这个Tcp连接就会保持着
而一个用户接收另一个用户的消息肯定是服务器推给用户的,而不是用户从服务器上面拉取的。
chatservice.hpp修改内容 加入了记录用户连接的userConnMap
为确保userConnMap的线程安全加入mutex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #ifndef CHATSERVICE_H #define CHATSERVICE_H #include <muduo/net/TcpConnection.h> #include <unordered_map> #include <functional> #include <mutex> #include "json.hpp" #include "usermodel.hpp" using namespace std;using namespace muduo;using namespace muduo::net;using json=nlohmann::json;using MsgHandler=std::function<void (const TcpConnectionPtr& conn,json &js,Timestamp)>;class ChatService { public : static ChatService* instance () ; void login (const TcpConnectionPtr& conn,json &js,Timestamp time) ; void reg (const TcpConnectionPtr& conn,json &js,Timestamp time) ; MsgHandler getHandler (int msgid) ; private : ChatService (); unordered_map<int ,MsgHandler> _msgHandlerMap; UserModel _userModel; mutex _connMutex; unordered_map<int ,TcpConnectionPtr> _userConnMap; }; #endif
chatservice.cpp修改内容 加的内容都是为了保证_userConnMap的线程安全操作
mysql操作线程安全由mysql server保证
json都是局部变量,线程的栈是自己的,所以不需要线程安全
可是把这些操作都放在锁的作用范围锁的粒度太大了,所以加个大括号,锁出了作用域自动释放资源了
1 2 3 4 5 6 7 8 { lock_guard<mutex> lock (_connMutex) ; _userConnMap.insert ({id,conn}); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 void ChatService::login (const TcpConnectionPtr& conn,json &js,Timestamp time) { int id=js["id" ].get <int >(); string pwd=js["password" ]; User user=_userModel.query (id); if (user.getId ()==id && user.getPwd ()==pwd) { if (user.getState ()=="online" ) { json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=2 ; response["errmsg" ]="该用户已经登录,请重新输入新账号" ; conn->send (response.dump ()); } else { { lock_guard<mutex> lock (_connMutex); _userConnMap.insert ({id,conn}); } user.setState ("online" ); _userModel.updateState (user); json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=0 ; response["id" ]=user.getId (); response["name" ]=user.getName (); conn->send (response.dump ()); } } else { json response; response["msgid" ]=LOGIN_MSG_ACK; response["errno" ]=1 ; response["errmsg" ]="用户名或者密码错误" ; conn->send (response.dump ()); } }