Author: Borage
Time: 17:27, August 9, 2021
Development environment: Ubuntu VSCode
Compiler: g++
Database: MySQL
Programming language: C++
GitHub link
Project overview
Complete a project similar to QQ. The main functions are registration, login, adding friends, creating groups, joining groups, one-to-one chat, group chat, etc.
We need server and client. In order to reduce the coupling between network module and business module, we use MVC architecture here.
Data module
Table design
As a chat project, we certainly need many tables to store some information. For example, when the client logs in, the server needs to match the corresponding account and password information in the library to verify identity. When the user registers, we also need to write the corresponding account number and password to the table.
user table
It is used to store user account, password, nickname, online status and other information
friend table
Used to store the user's friend information
allGroup table
It is used to manage all group information created by the server user
groupUser table
It stores the information of all group members in the group, including the roles of members, such as creator and normal
offlineMessage table
In addition to receiving information from other users online, users can also receive offline information. Offline information is stored in the offline message table and pushed to users after users log in.
Communication format
The communication between server and client uses JSON to complete the standard transmission of data in the network.
For different data, different formats are adopted, as follows:
1.Sign in json["msgid"] = LOGIN_MSG; json["id"] //User id json["password"] //password 2.Login feedback json["msgid"] = LOGIN_MSG_ACK; json["id"] //Login user id json["name"] //Login user password json["offlinemsg"] //offline message json["friends"] //Friend information, which has three fields: id, name and state json["groups"] //Group information, which contains three fields: id, groupname, groupdesc and users //users has four fields: id, name, state and role json["errno"] //The error field is set to 1 in case of error and 2 when the user is not online json["errmsg"] //error message 3.register json["msgid"] = REG_MSG; json["name"] //User name json["password"] //User name 4.Registration feedback json["msgid"] = REG_MSG_ACK; json["id"] //Return his id number to the user json["errno"] //Error message, failure will be set to 1 5.add friends json["msgid"] = ADD_FRIEND_MSG; json["id"] //Current user id json["friendid"] //id of the friend to add 6.One on one chat json["msgid"] = ONE_CHAT_MSG; json["id"] //Sender id json["name"] //Sender name json["to"] //Recipient id json["msg"] //Message content json["time"] //Sending time 7.Create group json["msgid"] = CREATE_GROUP_MSG; json["id"] //Group creator id json["groupname"] //Group name json["groupdesc"] //Group description 8.Join group json["msgid"] = ADD_GROUP_MSG; json["id"] //User id json["groupid"] //Group id 9.Group chat json["msgid"] = GROUP_CHAT_MSG; json["id"] //Sender id json["name"] //Sender name json["groupid"] //Sender name json["msg"] //Message content json["time"] //Sending time 10.cancellation json["msgid"] = LOGINOUT_MSG; json["id"] //id to log off
After specifying the communication format, we can start!
Network and service module
Network module
Here we use the interface provided by muduo library
When the user connects or disconnects, the preset callback function about connection will be called, and its execution object should be main reactor.
When a read-write event occurs, the onMessage method will be called, and the execution object is sub reactor. It is very important to decouple its content from the network module and business module!!!
There is a msgid field in the communication module, which represents the message type between communications
LOGIN_MSG = 1, //Login message, binding login LOGIN_MSG_ACK, //Login response message REG_MSG, //Register message, bind regist er REG_MSG_ACK, //Registration response message ONE_CHAT_MSG, //One to one chat message ADD_FRIEND_MSG, //Add friend message CREATE_GROUP_MSG, //Create group chat ADD_GROUP_MSG, //Join group chat GROUP_CHAT_MSG, //Group chat message LOGINOUT_MSG, //Logoff message
According to these message types, we can create a container for performing callback operations and bind the corresponding callback operations.
msg_handler_map_.insert({LOGIN_MSG, bind(&ChatService::login, this, _1, _2, _3)}); msg_handler_map_.insert({LOGINOUT_MSG, bind(&ChatService::loginout, this, _1, _2, _3)}); msg_handler_map_.insert({REG_MSG, bind(&ChatService::regist, this, _1, _2, _3)}); msg_handler_map_.insert({ONE_CHAT_MSG, bind(&ChatService::one_chat, this, _1, _2, _3)}); msg_handler_map_.insert({ADD_FRIEND_MSG, bind(&ChatService::add_friend, this, _1, _2, _3)}); msg_handler_map_.insert({CREATE_GROUP_MSG, bind(&ChatService::create_group, this, _1, _2, _3)}); msg_handler_map_.insert({ADD_GROUP_MSG, bind(&ChatService::add_group, this, _1, _2, _3)}); msg_handler_map_.insert({GROUP_CHAT_MSG, bind(&ChatService::group_chat, this, _1, _2, _3)});
Business module
//Store the connection of online users, so that the server can send messages to users and pay attention to thread safety unordered_map<int, TcpConnectionPtr> user_connection_map_; mutex conn_mutex_; UserModel user_model_; OfflineMessageModel offline_message_model_; FriendModel friend_model_; GroupModel group_model_;
Registered business
After obtaining JSON, the server executes the callback function of the registration business to query the user table. If the name is repeated, set errno to 1, set 0 successfully, package JSON and return it to the client.
Login service
After receiving the JSON, the server queries the user table. The account and password match fails. Set errno to 1. Check whether the user is online after success. If the current user's online status in the user table is offline, set errno2. If not, log in. Set errno to 0. At this time, push the offline information and user / group information to the client.
Add friends
After getting the deserialized string, insert the information directly into the friend table
One on one chat
After the server receives the client information, it first performs maintenance on the_ connectonMap to query. If it exists, it means that the target user is already in the server and can be forwarded directly.
If it does not exist, query the online status of the target user in the user table. If it is not online, store it in the offline message table (send offline information). If it is online, publish the information to the message queue middleware of redis.
Create group chat
After receiving the information, the server writes the group information into the allgroup table, and writes the Creator into the groupuser table as "creator".
Join group chat
After receiving the client information, the server writes the user into the groupuser table as "normal";
Group chat service
After receiving the information from the client, the server first goes to GroupUser to query the IDs of all group members, and then goes to the user of the server one by one_ connection_ map_ Whether the user receiving the information is online on this server. If online, it can be forwarded directly. If not, check whether the information in the database is online. If online, it means that the receiving user logs in to other servers and forwards the message through redis m id dleware.
If they are not online, send offline messages.
Cancellation business
After receiving the information, the server changes the user's online state to offline state.
Server cluster
There will be an obvious problem when we deal with this at present. Generally speaking, a server intelligently supports 1-2w concurrency, which is far from enough for a chat system. We use nginx load balancing here. Users still access the same port and nginx connects different customers to servers that do not work
Cross server communication
There is a problem after the cluster. How do two clients hanging on different servers communicate? We use redis's Message Queuing Middleware here.
When the client logs in, the server subscribe s its id number to the redis middleware, which indicates that the server is interested in the events of this id. when redis receives the message sent to this id, it will forward the message to this server.
reflection
This server also has some design defects:
-
If the number of users is large, you need to pull offline information and user / group status information from the data table of the server after each login, which will cause large access. How to save these information to the local client? When should this operation be completed
-
When the server suddenly goes down, what should the connected users do with this information?
-
Friends added in this project can be added successfully without the consent of the added user, which needs to be improved in the later stage.
reference
Shi Lei.Tencent classroom