上一篇Socket封装只是一个技术的封装,是为了隐藏一些技术细节,这一篇主要是通讯组件的逻辑封装。
这里基本没有什么可谈的,只是到处调用上一篇里的方法而已。比如读取配置连接远程主机啦,关闭Socket连接啦,组合字符串发送命令啦等。
这个地方最费事的是SocketPack的OnReceive事件的处理,这个事件在Socket接收到远程主机的命令时会触发。
private void winsock_OnReceive(object sender, ComReceiveEventArgs e) {
try
{
string msg = e.ComData;
logNetForInfo.Info("From [" + Host + "] Data:" + msg);
int commandEndIndex = 0;
int dataEndIndex = 0;
int oldDataIndex = 0;
string command = "";
string data = "";
while(true)
{
#region 解析指令与数据部分
commandEndIndex = msg.IndexOf(CommandConst.COMMANDTOKEN,dataEndIndex);
if (commandEndIndex < 0) break;
oldDataIndex = dataEndIndex;
dataEndIndex = msg.IndexOf(CommandConst.DATATOKEN,commandEndIndex);
if (dataEndIndex < 0) break;
if (oldDataIndex == 0)
command = msg.Substring(0,commandEndIndex);
else
command = msg.Substring(oldDataIndex + CommandConst.DATATOKENLENGTH,commandEndIndex - oldDataIndex - CommandConst.DATATOKENLENGTH);
data = msg.Substring(commandEndIndex + CommandConst.COMMANDTOKENLENGTH,dataEndIndex - commandEndIndex - CommandConst.COMMANDTOKENLENGTH);
#endregion
ProcessData(command,data);
}
}
catch(Exception ex)
{
logNet.Error("从 [" + Host + "]读取数据时发生错误:" + ex.Message);
}
}
上面没什么可以特别一书的东西,就是字符串运算,很笨的方法。然后下面处理解析得的数据
private void ProcessData(string command,string data)
{
#region 根据不同指令触发不同事件
switch(command)
{
case CommandConst.BEGINSIGNIN://开始签到
if (BeginSignInEvent != null)
ThreadingEvent.Invoke(BeginSignInEvent,new object[]{this,new BeginSignInEventArgs(true)});
break;
//......
//......略......
//......
case CommandConst.DOWNLOADDB://下载数据库
if (DownLoadDBEvent != null)
DownLoadDBEvent.BeginInvoke(this,System.EventArgs.Empty,new AsyncCallback(DownLoadDBResult),null);
break;
default:
if (DataReceive != null)//其他命令
ThreadingEvent.Invoke(DataReceive,new object[]{this,new ComReceiveEventArgs(data)});
break;
}
#endregion
}
我们注意到上头事件的触发有时没有用通常的写法,而是用ThreadingEvent.Invoke。因为在.net里在辅助线程上是无法执行方法来修改窗体UI的,要修改UI只能在窗体自身的线程上执行。由于系统中运用的一些组件多是在各个不同的线程中运行,甚至组件本身运行在很多的线程上,这其中有很多事件都涉及到UI的改变。而按我们通常的写法,那些事件代码都将会在触发这个事件的线程里执行,那样的话无法改变UI。所以写了个事件触发的通用写法,只要有可能涉及到修改UI的事件都通用它来触发。
public sealed class ThreadingEvent
{
public static void Invoke(Delegate eventDelegate,object[] param)
{
Control ctr = eventDelegate.Target as Control;
if (ctr != null)
{
if (ctr.IsHandleCreated)
ctr.Invoke(eventDelegate,param);
}
else
eventDelegate.DynamicInvoke(param);
}
}
有点不完善的地方,没有再写一个用于异步调用的方法,因为在else这个条件时,Delegate的异步调用不知该怎么写。不过还好现在看来那些同步触发的事件也没造成系统的什么大问题,所以就这样将就用了。
最后不要忘了,还有一个Socket连接可用性的监测,如果有变化,还记得要向外部触发事件,这个比较简单,就不去讲述了。