循序渐进做项目系列(2):最简单的C/S程序——消息异步调用与消息同步调用

时间:2021-09-04 20:53:02
   上篇博客 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法 实现了一个最简单的C/S程序,即让服务器来做加法。当时为了通俗易懂采用了消息异步调用的方式。今天我们要采用消息同步调用的方式来实现,并且对比一下两种方式的优劣。通过这个实例也能让对于“同步调用异步调用”不甚了了的朋友们对于这一对概念有一个初步直观的认识。
  究竟什么是消息同步调用什么是消息异步调用呢?

一·消息异步调用

  对于这个问题我们先不急于从原理上回答,先来看下上一次客户端向服务端发送消息的代码。
        private void button1_Click(object sender, EventArgs e)
{
int leftNum = int.Parse(this.textBox_leftNum.Text);
int rightNum = int.Parse(this.textBox_rightNum.Text);
//将数据转码以备发送给服务端
byte[] leftNumCode = BitConverter.GetBytes(leftNum);
byte[] rightNumCode = BitConverter.GetBytes(rightNum);
byte[] NumbersCode = new byte[];//用来合并待发送的消息(一个整型占4个字节)
for (int i = ; i < ; i++)
{
NumbersCode[i] = leftNumCode[i];//第一个数的编码装进前四位
}
for (int j = ; j < ; j++)
{
NumbersCode[j + ] = rightNumCode[j];//第二个数的编码装进后四位
}
//发送给服务器,100代表做加法这件事
Program.clientEngine.CustomizeOutter.Send(
, NumbersCode);
}

关键是客户端发消息的那句代码,发完消息后客户端就撒手不管了,究竟服务端有没有收到呢?会不会发出去的消息就是“肉包子打狗”呢?客户端不得而知。反正皮球踢给了服务端,好吧服务端接了,然后服务端是这样做的:

 //该方法用来处理客户端通过clientEngine.CustomizeOutter.Send()方法发送过来的消息
public void HandleInformation(string sourceUserID, int informationType, byte[] info)
{
if (informationType == )
{
int leftNum = BitConverter.ToInt32(info, );//将字节数组前四位还原成leftNum
int rigthNum = BitConverter.ToInt32(info, );//将字节数组后四位还原成rigthNum
int result = leftNum + rigthNum;
byte[] resultCode = BitConverter.GetBytes(result);//将计算结果转码
this.serverEngine.CustomizeController.Send(sourceUserID, , resultCode);//发送给对应的客户端
}
}

服务端处理一番后再度发消息给客户端,又一个皮球踢了出去。客户端接球:

 //该方法来处理服务端Send()方法发送过来的消息
public void HandleInformation(string sourceUserID, int informationType, byte[] info)
{
if (informationType == )
{
MessageBox.Show("结果为:" + BitConverter.ToInt32(info, ));
}
}

综上就是消息异步调用,就是发送者只发消息,仅此而已,发完消息该干嘛干嘛,接受者接收消息后再向发送者发消息时,自身角色同时也转换为发送者,按发送者的风格行事。但是从上述过程中可以看出,采用消息异步调用会使得通信过程较为复杂,需要反复“收发”消息。

二·消息同步调用

我们再来看看消息同步调用是怎么做的:

        private void button1_Click(object sender, EventArgs e)
{
int leftNum = int.Parse(this.textBox_leftNum.Text);
int rightNum = int.Parse(this.textBox_rightNum.Text);
//将数据转码以备发送给服务端
byte[] leftNumCode = BitConverter.GetBytes(leftNum);
byte[] rightNumCode = BitConverter.GetBytes(rightNum);
byte[] NumbersCode = new byte[];//用来合并待发送的消息,一个整型占4个字节
for (int i = ; i < ; i++)
{
NumbersCode[i] = leftNumCode[i];//第一个数的编码装进前四位
}
for (int j = ; j < ; j++)
{
NumbersCode[j + ] = rightNumCode[j];//第二个数的编码装进后四位
}
//发送给服务器请求获取结果
byte[] resultCode = Program.clientEngine.CustomizeOutter.Query(, NumbersCode);
int result = BitConverter.ToInt32(resultCode, );//解析
MessageBox.Show("结果为:" + result);
}

重点是发消息语句,与前面的“异步调用”的区别就在于请求返回结果,然而处理过程并不是在客户端的主机上完成的,而是在服务端:

   //该方法用来处理客户端发送过来的请求,即clientEngine.CustomizeOutter.Query()方法发送过来的消息
public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info)
{
if (informationType == )
{
int leftNum = BitConverter.ToInt32(info, );//将字节数组前四位还原成leftNum
int rigthNum = BitConverter.ToInt32(info, );//将字节数组后四位还原成rigthNum
int result = leftNum + rigthNum;
byte[] resultCode = BitConverter.GetBytes(result);//将计算结果转码
return resultCode;
}
return null;
}

表面上类似于调用本地方法,但实质上是一个分布式处理过程。在服务端处理信息的过程中,客户端一直处于等待状态,直到服务端处理完毕返回结果。假使该语句下还有代码块,则在等待过程中不能够执行。这就是消息同步调用。

三·总结

消息同步调用与消息异步调用的概念脱胎于同步调用与异步调用的概念,相当于是对于原概念的拓展。同步调用与异步调用是对于方法的调用而言,所谓同步调用,就是在调用一个方法时,在没有得到结果之前,主调线程处在等待状态;所谓异步调用即,一个方法被调用后,主调线程不用等待结果返回。我们这里所谈的消息同步调用与消息异步调用是针对于“发送/回复”这种通信的逻辑模型而言,能够立即获得回复的发送称为消息同步调用,反之称为消息异步调用。

类似于方法同步调用,消息同步调用也会阻塞当前调用线程,但是由于其模型简单直观,而且将“发送“与”回复”严格匹配,在其适用的场合较消息异步调用模型具有优越性。

毫无疑问,在通信框架中,原始的模型就是异步调用模型,而ESFramework也增加了同步调用的机制,使得编程模型更加丰富。

至于消息同步调用与消息异步调用在实际的项目中如何运用来实现更复杂的需求,完成更强劲的功能,我会在该系列以后的博文中继续探讨,支持的朋友请顶一顶,给与我坚持不懈的力量!

四.源码下载

  让服务器做加法2--同步调用模型