minetest源码解析六:Client与Server数据传递(Client端)
2017-08-17 23:41
453 查看
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 |
+-----------------------------+ | |
\-----------------/
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 |
+-----------------------------+ | |
\-----------------/
相关文章推荐
- minetest源码解析七:Client端更新流程
- client提交json(nodejs)和server解析json并返回json数据(express)
- minetest源码解析八:ClientEnvironment
- Kafka源码深度解析-序列12 -Server核心组件之2-ReplicaManager核心数据结构与Replica同步原理
- [源码]解析 SynchronousQueue 上界,下界.. 数据保存和数据传递. 堵塞队列. 有无频繁await?
- minetest源码解析一:mymain流程图以及核心函数介绍
- Hadoop源码分析HDFS Client向HDFS写入数据的过程解析
- minetest源码解析二:GUIEngine初始化、菜单刷新流程以及核心函数介绍
- Minetest源码分析十二:ServerMap
- appengine project中 client和server之间实现数据的传递
- Hadoop源码分析HDFS Client向HDFS写入数据的过程解析
- httpClient post步骤 解析json数据(向服务器传递,接受服务器传递))
- ZooKeeper源码解析(四):client如何和server连接
- Camera源码解析之数据传递
- spice源码解析之 client-server 通信机制
- Android源码解析之Binder中Server和Client获得Service Manager接口
- Eureka 源码解析 —— Eureka-Server 启动(一)之 ServerConfig
- HttpClient获取并解析JSON数据
- jQuery源码解析-----jQuery数据缓存系统
- 注册中心 Eureka 源码解析 —— Eureka-Client 初始化(一)之 EurekaInstanceConfig