先介绍故事背景,本人是一名15年老程序猿,从Logo到Basic到猥琐Basic到C++到C++++(C#)
最终使用C#已经13个年头,做网络通信也有10个年头了,自己写的tcp、udp各种rtu协议,modbus协议的封装类都有过,一开始用Socket自己封装类,后来发现TCPClient和UDPClient也挺好用的,项目中也用了无数,不过以前一直是机机交互,从来没出现过问题。
最近我看不惯公司的软件,申请立案重新做一个,用了CS架构,全UDPmodbus通信,UDPmodbus也是我这15年来的经验表明这是最好用的网络通信(不要跟我讲什么TCP三次握手,老夫用网都是UDP一把梭),历经一年制作完成了初代版本,虽然界面操作等各种bug层出不穷,但是我一直以为,网络部分是最稳定的,毕竟是我最新研发的UDP多线程上传下载并行异步modbus封装类。
结果上了一个大项目,服务器CPU负载异常的高,经常卡死(服务器程序完全死掉无响应)。
先展示代码,
UDP初始化
UDPClient udpcon = new UdpClient(一个写了端口的 IPEndPoint);
udpcon.BeginReceive(new AsyncCallback(ReadCallback), 0);
然后是那个ReadCallback异步回调函数
static void ReadCallback(IAsyncResult ar)
{
IPEndPoint dqip = new IPEndPoint(IPAddress.Any, 1);
byte[] buf = new byte[1];
try
{
buf = udpcon.EndReceive(ar, ref dqip);
}
catch
{
}
restart:
try
{
udpcon.BeginReceive(new AsyncCallback(ReadCallback), 0);
}
catch
{
goto restar;
//这个操作也是以前总是监听中断收不到数据了,后来在网上找到的大神解决方案,
//当时也是觉得这个操作666,果然加了这个goto以后,我的服务器再也没中断过了。
}
调个处理函数进行数据处理(buf);
}
//=============================================================
上面这个简单的逻辑我用了大约六七年,没有任何问题,包括做在线采集,上百采集器,服务器每秒好几千个udp包都没有问题
发现经常卡死之后,断点跟踪,开一大群客户端测试,结果发现,我以前觉得666的操作,goto restart每次都失败,就每次都goto,这等于一个死循环线程,只要出现1次异步回调失败就会增加这么一个死循环,任务管理器一看妈耶!那个线程数卡卡增长,很快就上万了,然后这个服务器就卡死了,于是我进行了一次重大升级,不废话,直接上代码
try
{
udpcon.BeginReceive(new AsyncCallback(ReadCallback), 0);
}
catch
{
try
{
udpcon = new UdpClient(ipbd本地地址);
udpcon.BeginReceive(new AsyncCallback(ReadCallback), 0);
}
catch
{
}
}
那个goto改成了这样,果然,立刻就解决了服务器卡死问题。
结果挂了几天,发现服务器总是登陆的人一多,就收不到数据了,程序没死,界面按钮等操作都正常,就是UDP收不到了,找了几个人帮我一起测试了一整天,终于发现,在catch里面重新监听还是有可能会失败,我给第一次BeginReceive的catch加了个Exception看了一下错误原因,居然是【远程主机强迫关闭了一个现有的连接】
我擦!凸(艹皿艹 )UDP是无连接协议,这地球人都知道,从上学第一次学网络通信就讲过,为什么会出现这个情况呢?而且用了这么多年网络通信的我居然没遇到过,结果发现,这是因为通信没有完成之前,客户端突然断开,服务器就会收到这个消息,特别是在客户端网络不好的情况下,就更容易出现,我自己测试因为都是本机,几乎不存在这个情况,而且以前用的机机通信也不存在客户端会关闭一说,都是长开的,所以我一直没遇到这个问题。
经过大约4个小时的搜索,终于找到了解决方案……
创建UDP监听之后,一定要做下列操作
在初始化对象后设置属性如下:
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
你的udp.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] {Convert.ToByte(false)}, null);
大概意思就是给当前连接创建基于Socket的最底层的通信过程。下面是搜索到的那篇文章,那文章我发现好多人转载,我就不转载了,直接贴个地址。有兴趣的自己去看看。
地址:https://www.cnblogs.com/liuslayer/p/7867239.html