个人项目QXin局域网通讯工具|分层架构的体会
时间:2010-08-21 来源:Xingg
项目名称:QXin局域网通信 (兼容飞鸽,飞秋等局域网通信)
程序架构:
WinForm .net 4.0
功能点:
一、日常提示
上下线提示,更新获取局域网QXin用户列表,正在输入提示、类QQ界面模式
二、字符串通信
单用户字符串通信、多用户字符串广播
三、文件通信
单用户单文件传送,单文件夹传送、多文件多文件夹混合传送。
多用户广播传送
程序实现过程:
UDP通信:(上下线提示,字符串通信)
一发一回的方式,例如:用户A上线,以广播的方式发送“我上线啦”的消息,其他用户接收后把该用户加添到用户列表里,返回一个“已收到”信息,用户A接收到各个用户返回的信息并一一加载到用户列表。
TCP通信:(文件传送)
首先用户A以UDP方式通知用户B,”我要发送某某文件”,用户B接收后,AB用户开始进行TCP连接,然后发送文件
单文件发送和文件夹发送差别很大,由于文件夹不是文件,只能通过发送消息通知远程用户在什么地方建立一个文件夹,然后送该文件夹下的文件。如果该文件夹有子文件夹,又系同样的方式通知远程用户在哪个文件里新建子文件夹,不断递归。
(PS:飞鸽协议是开源的,不过飞秋可不是,飞秋虽然同样采用飞鸽协议,但是在此基础上增加修改了不少内容,要兼容飞秋需要用WPE抓包分析飞秋的数据包。)
分层架构模式思想:
所谓分层架构,分层架构并不是一个程序分几个模块或者源文件就是分层架构,分层架构是一种模式,分层架构必须只能自上往下操作,下层不知上层的内容,上层只能调用其下层的函数,UI层调用BLL层,BLL层调用DAL层. 而BLL层是绝不可以调用UI层的. 例如BLL层绝不能出现MessageBox等winform方法。而且,要做到BLL和DAl 在脱离UI表面层后,转到控制台通过调用其类和方法也能正常运行,才算好的分层架构。
编写体会总结:
在编写QXin的过程中,实现功能的同时要时刻兼顾分层结构的模式确实带来不少的麻烦,很多时候,在完成某个功能点后,发现UI层还带着BLL层的操作,就得重写一次,把属于BLL的操作剥出来,好在VS有封装方法,替换名字的贴心功能.
某些遇到过的问题:
第一:文件传输进度显示问题
文件传输属于BLL,同时必须开辟多线程与远程端口通信, BLL层又不能直接对UI层的ProgressBar等控件进行操作,如何实时反馈给UI层呢?
猜想一:方法返回值形式,这个首先否定了, 以方法返回值形式是行不通的,文件传输比较开辟线程,UI层调用BLL层以后就继续往下执行了,不会等线程方法返回。结论:不能使用。
猜想二:以变量参数指针方式传给方法,在传输文件的线程中不断给变量赋值,UI便可从中取出传输文件的信息,方可显示到Progressbar上。 但是要实时显示进度还必须开辟一个线程对Progressbar进行控制,而且两个线程对同一个变量操作很容易造成并发问题。结论:不是最佳。
猜想三:以委托方式(C#)作为方法的参数传给入BLL层,在BLL层上传参数调用此委托/方法,即可即保持分层架构,亦可以对UI进行间接控制。结论:最佳。
Q1:猜想三那BLL还不是操作了UI层,怎算保持分层架构?
A1:对控件操作的方法/方法定义还是保持在UI层上,例如在WinForm下UI层定义此方法包含Messagebox方法,换到控制台里运行,改成Console.WriteLine方法,不必修改BLL就可以用了。
第二:多线程回收问题
在多文件传输中,如:
UI中:
private void btSend_Click(object sender, EventArgs e) //只对UI层进行直接操作
{
Thread ThreSendFiles =new Thread(new ThreadStart(delegate() {
Foreach(ListViewItem Item in ListView.SelectItems) //遍历选择的文件发送
{
QXin. SendFile((QXinFile) Item.Tap); //调用BLL的发送文件方法
}
}));
ThreSendFiles. IsBackground=true;
ThreSendFiles.Start();
}
BLL中:
void SendFile (QXinFile fileItem)
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, MyPoint);
tcpListener.Start();
Socket TcpConnect = tcpListener.AcceptSocket();
//…..
TcpConnect.Dispose();
tcpListener.Stop();
}
假定我想实现在传输过程中,按取消按钮,马上断开连接,停止线程运行。于是我在
UI中:
private void btCancelSend_Click(object sender, EventArgs e)
{
ThreSendFiles.Abort();
ThreSendFiles = null;
GC.Collect();
}
原本以为结束线程,调用GC.Collect()后就万事OK,结果当再次发送文件的时候,抛出端口不能重复绑定的异常。原来线程还在运行。思来想去,最好的方法是把TcpListener 和Socket等类定义为字段,在BLL中提供方法供UI断开连接。
BLL中:
TcpListener tcpListener;
Socket TcpConnect;
public void Collect()
{
if (tcpListener != null)
{
tcpListener.Stop();
}
if (TcpConnect != null)
{
TcpConnect.Dispose();
}
}
UI中:
private void btCancelSend_Click(object sender, EventArgs e)
{
ThreSendFiles.Abort();
ThreSendFiles = null;
QXin.Collect();
}
以上是我对分层架构模式的理解,如有错误,望请指点。