微风送爽,五月份的天气还是有几分寒意。待在家里,套上双袜子,也不是十分冷。外面飘着雨,哗啦啦的拍打着窗户,春雨告诉我夏天就要到了。我爱夏天,因为这是女人的舞台,我只不过是一个看客。夏天常备各种凉茶。
车辆监控这个项目是我进入A公司第一个,网络通信没有开发过,是个挑战。项目简单来说是通过平台可以实时了解车辆的位置/油耗/方位。车辆是否越界/偏离。这需要每辆车安装车载设备,当然这个设备和我无关。我只需提供一个IP地址,两个端口号。
网络通信包括两部分:TCP通信/UDP通信,泛泛来说各有优缺。比如TCP通信,它是稳定的,当客户端与服务端建立连接后,其之间的每条通信都能确保收到。它也有缺点,当发送信息频率过快,或者网络不稳定时,会出现粘包(多条数据)/残包(数据片段)现象。这时就需要我们主动拆分或拼接数据。
UDP是不稳定的,客户端或服务端执着的发送数据,它们对结果不管不问,数据是否被对方接收是未知的。这时我们需要一个机制,当客户端发送数据,服务端收到必须予以回复表示“我已接受到数据”。相较于TCP通信,它的每条数据都是独立的。
车辆监控项目是一个B/S架构的网站,它运行在iis上。它即是web开发,我们不得不面对这些问题。
1.页面程序与通信服务是怎样的关系?
当然页面程序与通信服务是相对独立的,它们唯一关联的是同应用于一个Appliction中。在实际运行中,通信服务接收数据,解析到数据库。页面程序从数据库中读取,它们时候不打交道。
2.通信服务我写在哪里,页面程序与通信服务如果相互调用?
既然通信服务相对独立,同时也为了适用性,我将通信封装在单独的程序集中,这个程序集暴露出两个类TCPServer/UDPServer 。web项目中引用,打开Global.asax文件并写下这样一些代码。
public class Global : System.Web.HttpApplication { TCPServer tcp; UDPServer udp; protected void Application_Start(object sender, EventArgs e) { //TCP通信服务启动 tcp=new TCPServer(); tcp.Start(); //UDP通信服务启动 udp=new UDPServer(); udp.Start(); } protected void Application_End(object sender, EventArgs e) { tcp.Stop(); udp.Stop(); } }
通信服务开始运行,端口开始监听。
但是我如何通过点击web页面的某个按钮来给客户端发信息呢?
想发送信息必须知道给谁发,这里我们必须知道客户端的IP和端口号。所以我们必须维护一个客户端列表,要实现这个功能我必须新建类,客户端的类(Client)。客户端类包括两个对象,一。客户端key,用以识别客户端。二。Socket对象,用以存储客户端的socket。做完这些后,我在TCPServer中新建静态对象List<Client> ClientList。每当新的客户端连接时就Add到泛型对象中。失去连接时,移除。
就这样我们可以通过下面下面代码为指定客户端发送信息。
TCPServer.ClientList.Find(t=>t.Key=="****").Socket.Send(byte[]);
那么在通信服务程序集中要如何写,会遇到那些难题呢?
TCP通信需要做的是监听端口/接收数据,这俩种操作都是无限循环,所以必须使用多线程,不能让程序停在某一处。异步处理,C#已经给我提供很好的方法。我们只要写写业务逻辑就好。对于客户端断开连接,我是用try catch捕获异常来确定的。
对于如何识别客户端,我们前面提到了key。客户端每次发送数据时,数据中都会包含类似key的唯一识别码,我们用这个来识别是那一客户端。
对于如何处理粘包/残包,客户端的数据中会用特定的字符来标明头和尾,我们用这个来拆分数据。客户端发送的数据有明确的排列格式,这种特定的格式,我们称之为协议。
UDP通信难点在于异步通信,还有收到数据立即返回答复,问题不大。
至此项目核心解说完毕,当初异步通信是最让人头痛的地方,修改很多遍,而且当时没有现成的工具。现在好了,可以百度“TCP调试助手”下载测试。
本文不涉及实际代码,只谈实现方法及解决方案。