minetest源码解析六:Client与Server数据传递(Client端)
1.Client与Server数据通信
Client与Server端的数据通信不是同步的,是异步进行的,数据都存放在Connection对象中。Client和Server都是在各自的线程中自己不停地去获取数据、发出数据命令。
Connection minetest->connection.h
Server服务端
void Server::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
// Limit a bit
if(dtime >2.0)
dtime =2.0;
{
JMutexAutoLock lock(m_step_dtime_mutex);
m_step_dtime += dtime;
}
// Throw if fatal error occurred in thread
std::string async_err =m_async_fatal_error.get();
if(async_err !=""){
throwServerError(async_err);
}
}
Server:ServerThread
minetest->server.cpp
void * ServerThread::Thread()
{
...
while(!StopRequested())
{
m_server->AsyncRunStep();
m_server->Receive();
...
}
...
}
Client客户端
void Client::step(float dtime)
{
ReceiveAll();
Packet counter
UGLY hack to fix 2 second startup delay caused by non existentserver client startup synchronization in local server or singleplayer mode
Run Map's timers and unload unused data
Handle environment
Send player position to server
Replace updated meshes
Load fetched media
If the server didn't update the inventory in a while, revert the local inventory.
Update positions of sounds attached to objects
Handle removed remotely initiated sounds
}
2. Connection 类
Connection类中有两个类:friendclass ConnectionSendThread、friendclass ConnectionReceiveThread。
这两个类在创建Connection时,都会创建出来且都会start。在创建客户端时就会创建这两个对象并运行这两个线程。
m_sendThread.Start();
m_receiveThread.Start();
3.服务端发给客户端的命令存哪?Connection::m_event_queue
void * ConnectionReceiveThread::Thread()
{
ThreadStarted();
。。。
while(!StopRequested()) {
/* receive packets */
receive();
。。。
return NULL;
}
void ConnectionReceiveThread::receive()
{
。。。
/* first of all read packets from socket */
/* check for incoming data available */
m_connection->putEvent(e);
。。。
}
void Connection::putEvent(ConnectionEvent &e)
{
assert(e.type !=CONNEVENT_NONE);
m_event_queue.push_back(e);
}
m_event_queue:对客户端来说,存放接收服务端发给客户端的信息 TOClient**。
minetest->connection.h
MutexedQueue<ConnectionEvent> m_event_queue;
enum ConnectionEventType{
CONNEVENT_NONE,
CONNEVENT_DATA_RECEIVED,
CONNEVENT_PEER_ADDED,
CONNEVENT_PEER_REMOVED,
CONNEVENT_BIND_FAILED,
};
客户端通过Client::step()->Client::ReceiveAll()->Client::Receive()->m_con.Receive(sender_peer_id, data)->Connection::waitEvent(m_bc_receive_timeout)获取。
4. 客户端发给服务端的命令存哪?Connection::m_command_queue
void Client::Send(u16 channelnum,SharedBuffer<u8> data,bool reliable)
{
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
}
void Connection::Send(u16 peer_id,u8 channelnum,
SharedBuffer<u8> data,bool reliable)
{
assert(channelnum <CHANNEL_COUNT);
ConnectionCommand c;
c.send(peer_id, channelnum, data, reliable);
putCommand(c);
}
void Connection::putCommand(ConnectionCommand &c)
{
if (!m_shutting_down)
{
m_command_queue.push_back(c);
m_sendThread.Trigger();
}
}
ConnectionSendThread发送Command TOSERVER**
void * ConnectionSendThread::Thread()
{
ThreadStarted();
while(!StopRequested() ||packetsQueued()) {
。。。
/* translate commands to packets */
{
ConnectionCommand c =m_connection->m_command_queue.pop_frontNoEx(0);
while(c.type !=CONNCMD_NONE)
{
if (c.reliable)
processReliableCommand(c);
else
processNonReliableCommand(c);
c = m_connection->m_command_queue.pop_frontNoEx(0);
}
}
}
。。。
returnNULL;
}
5.clientiface.h
* State Transitions
Start
(peer connect)
|
v
/-----------------\
| |
| Created |
| |
\-----------------/
|
|
+-----------------------------+ invalid playername, password
|IN: | or denied by mod
| TOSERVER_INIT |------------------------------
+-----------------------------+ |
| |
| Auth ok |
| |
+-----------------------------+ |
|OUT: | |
| TOCLIENT_INIT | |
+-----------------------------+ |
| |
v |
/-----------------\ |
| | |
| InitSent | |
| | |
\-----------------/ +------------------
| | |
+-----------------------------+ +-----------------------------+ |
|IN: | |OUT: | |
| TOSERVER_INIT2 | | TOCLIENT_ACCESS_DENIED | |
+-----------------------------+ +-----------------------------+ |
| | |
v v |
/-----------------\ /-----------------\ |
| | | | |
| InitDone | | Denied | |
| | | | |
\-----------------/ \-----------------/ |
| |
+-----------------------------+ |
|OUT: | |
| TOCLIENT_MOVEMENT | |
| TOCLIENT_ITEMDEF | |
| TOCLIENT_NODEDEF | |
| TOCLIENT_ANNOUNCE_MEDIA | |
| TOCLIENT_DETACHED_INVENTORY | |
| TOCLIENT_TIME_OF_DAY | |
+-----------------------------+ |
| |
| |
| ----------------------------------- |
v | | |
/-----------------\ v |
| | +-----------------------------+ |
| DefinitionsSent | |IN: | |
| | | TOSERVER_REQUEST_MEDIA | |
\-----------------/ | TOSERVER_RECEIVED_MEDIA | |
| +-----------------------------+ |
| ^ | |
| ----------------------------------- |
| |
+-----------------------------+ |
|IN: | |
| TOSERVER_CLIENT_READY | |
+-----------------------------+ |
| async |
v mod action |
+-----------------------------+ (ban,kick) |
|OUT: | |
| TOCLIENT_MOVE_PLAYER | |
| TOCLIENT_PRIVILEGES | |
| TOCLIENT_INVENTORY_FORMSPEC | |
| UpdateCrafting | |
| TOCLIENT_INVENTORY | |
| TOCLIENT_HP (opt) | |
| TOCLIENT_BREATH | |
| TOCLIENT_DEATHSCREEN | |
+-----------------------------+ |
| |
v |
/-----------------\ |
| |------------------------------------------------------
| Active |
| |----------------------------------
\-----------------/ timeout |
| +-----------------------------+
| |OUT: |
| | TOCLIENT_DISCONNECT |
| +-----------------------------+
| |
| v
+-----------------------------+ /-----------------\
|IN: | | |
| TOSERVER_DISCONNECT |------------------->| Disconnecting |
+-----------------------------+ | |
\-----------------/