本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程。本产品已经成熟稳定并投入商用。
私有化部署免费下载:https://docs.shengxunwei.com/Post/f7bc8496-14ee-4a53-07b4-08d8e3da6269/553293a8-dfa1-4282-bc3f-96c6c623fc9a
客服系统开发过程中,最让我意外的是对 TCP/IP 协议的认识。过去一直认为 TCP/IP 是可靠的连接,加上过去开发的软件网络环境比较稳定,很少在这个问题上纠结。
直到客服系统的客户越来越多,才重新让我认识了基于 TCP/IP 协议的软件应该如何设计开发。
有许多客户做的是外贸业务,服务器部署在海外,比如香港、韩国、美国等,有些客服之前用基于网页的客服系统,最为困扰的问题就是丢消息!而使用我的客服系统,做到了100%稳定,不丢客户不丢消息。
本文将分几个部分,详细介绍基于 TCP/IP 协议开发时,应该如何考虑复杂网络环境下的消息传输。
演示网络中断,直接禁用网卡,或者手机进入飞行模式,也不丢消息,不出异常。
视频地址:https://v.youku.com/v_show/id_XNTEwNzQ5Mzg2OA==.html
TCP 报文的确认机制
首先我们回顾一下 TCP 协议,TCP 报文格式一般如下所示:
其中的 ACK ,表示对报文是否送达的一个回应。
ACK是TCP标头中的标志和字段。 发送一个消息至少需要一个标头,再加上所有较低层的内容。
下图则显示了 TCP 通信时,客户端和服务端之间报文传送的过程。
从图中可以看到,发出的消息,和回应的消息,都会有一个编号,如:#1、#2
在ACK报文回应时,它回附带上所收到的报文的编号,那么发送端只需根据收到的ACK报文中的编号,就能判定报文是否送达,已经所送达的数据包。如果在一定时间内,没有收到回应的ACK消息,则发送端会在一定时间内重新尝试发送。
基于 TCP 协议自有的消息确认机制,我们在上层应用中实现可靠的通信就比较简单了。底层通信相关的类已经帮我们实现好了可靠的 TCP 传输,一旦出现网络异常,我们在上层都能够收到相应的通知。
客户端自身网络异常这种情况最好处理。因为客户端程序异常退出会直接引发 ConnectionReset 的 Socket 异常。我们只需要在服务端捕获这个异常进行处理即可:
public bool Send(byte[] data)
{
// 连接已经断开了
try
{
_networkStream.Write(data, 0, data.Length);
}
catch (Exception ex)
{
OnDisconnected(ex);
return false;
}
return true;
}
网络链路异常
对于这种情况,我们只需要检测 Socket 对象的 Connected 属性。
但是需要特别注意:Socket 对象的 Connected 属性获取从 Socket 最后一个 i/o 操作到的的连接状态。 当它返回时 false , Socket 要么从未连接,要么不再处于连接状态。当 Socket 从另一个线程断开连接时,它可能会在操作中止后返回。
如果需要确定连接的当前状态,请发出非阻止的零字节发送调用。 如果调用成功返回或引发 WAEWOULDBLOCK 错误代码 (10035) ,则套接字仍处于连接状态;否则,将不再连接套接字。
我们可以通过实现一个定时心跳,来对网络链路进行检测:
_heartbeatTimer = new Timer((state) =>
{
HeartbeatMessage heartbeatMessage = new HeartbeatMessage();
Send(heartbeatMessage);
}, null, 3000, 3000);
在定时器发送心跳时,如果网络链路中断,我们可以收到以下消息:
private void _socketClient_Disconnected(object sender, EventArgs e)
{
if (_heartbeatTimer != null)
_heartbeatTimer.Dispose();
if (_socketClient != null)
{
_socketClient.Close();
_socketClient = null;
}
}
只需针对 Disconnected 事件,进行处理,将两端的状态,置于等待即可。
请访问:https://kf.shengxunwei.com