socket通讯问题!

wszd0 2016-05-08 10:02:53
我现在做一个项目,用c#开发一个服务器,通过socket与几十台设备通讯,每台设备一个IP,每秒钟读取一次设备的数据。现在遇到一个问题,如果遇到多台设备断电或断网,软件会出现部分卡死现象,设备恢复正常也不能再读取设备数据。也不是每次断网都这样,偶尔会发生,重启服务器可以恢复,现场要求服务器24小时不能停,所以服务器轻易不能重启。用到了try-catch,但是捕捉不到错误,我感觉是在发送接收数据的瞬间断网,导致通讯线程无法关闭造成的卡死。为什么会出现这样的现象,有没有解决的办法?大家看一下这部分程序有问题吗?
 public byte[] SSend(byte[] tosend, out SocketError err, int timeout = 1000)
{
err = SocketError.Success;
if (!_connected || _socket == null || !_socket.Connected)
{
err = SocketError.NotConnected;
IsConnected = false;
return null;
}
lock (this)
{
if (_socket == null) return null;
try
{

_socket.Send(tosend);
_recvBuffer = new byte[BUFF_SIZE];
if (_DEBUG) log.DebugFormat("> {0,3}: {1}", tosend.Length, ValueHelper.BytesToHexStr(tosend));
_sendTimeoutEvent.Reset();

_recvLen = 0;
IAsyncResult iar = _socket.BeginReceive(_recvBuffer, 0, BUFF_SIZE, SocketFlags.None, out err,
new AsyncCallback(receiveCallback), _socket);

if (!_sendTimeoutEvent.WaitOne(timeout, false))
{
_socket.EndReceive(iar);

//Error = string.Format("超时: {0}", timeout);
//log.ErrorFormat("< 超时: {0}", timeout);
}

if (_recvLen > 0)
{
if (_DEBUG)
log.DebugFormat("< {0,3}: {1}", _recvLen, ValueHelper.BytesToHexStr(_recvBuffer, _recvLen));
}
byte[] outBuff = new byte[_recvLen];
Array.Copy(_recvBuffer, outBuff, _recvLen);
return outBuff;
}
catch (Exception e)
{
Error = string.Format("异常: {0}", e.Message);
log.ErrorFormat("SSend erro: {0}", e.Message);
return null;
}
finally
{

}
}
}

private void receiveCallback(IAsyncResult ar)
{
try
{
_recvLen = _socket.EndReceive(ar);
}
catch (Exception e)
{
Error = string.Format("异常: {0}", e.Message);
//log.ErrorFormat("< 错误: {0}", e.Message);
}
finally
{
_sendTimeoutEvent.Set();
}
}
...全文
230 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
飞天凤凰601 2016-05-10
  • 打赏
  • 举报
回复
引用 14 楼 wszd0 的回复:
[quote=引用 12 楼 oysy 的回复:] 刚才去看了一下,已经正常运行25天多了
我这个通讯的设备有自己的协议,必须先发个请求才给回复数据。问题出在收发数据的瞬间断网,造成卡死。我找到一些问题,改了一下,目前还没有发现异常。[/quote] 我这也有有请求方式呀,定时请求,下面才回复数据,也没有出过问题……
飞天凤凰601 2016-05-09
  • 打赏
  • 举报
回复
刚才去看了一下,已经正常运行25天多了
飞天凤凰601 2016-05-09
  • 打赏
  • 举报
回复
我做的一个上位机程序,也是用tcp通讯的,下面传数据用的是GPRS,设计是5000台同时发数据上来, 现在至少连接200台还没有发生过问题..
  • 打赏
  • 举报
回复
如果你使用“轮询、阻塞”的思路,甚至用异步编程语法去偷运着通讯顺序之事,那么你的代码不但比真正异步程序代码更长、更大,而且更纠结。特别是当你把 _sendTimeoutEvent 这类东西的控制范围稍微“扩大一点”时,就更容易死锁了。 并且windows中线程资源耗费巨大,不应该使用“轮询、阻塞”方式去实现并发多线程设计。应该用真正异步的思路去实现异步流程设计,而不是伪异步的方式。
  • 打赏
  • 举报
回复
甚至用异步编程语法去偷运着通讯顺序之事 --> 甚至用异步编程语法去偷运着同步顺序之事
  • 打赏
  • 举报
回复
用异步操作语法、偷运着同步顺序流程设计,这种编程习惯的结果是肯定会产生许许多多的“死锁”问题。如果你能删除 _sendTimeoutEvent 而重新从自己的(比写代码更重要的)设计蓝图出发考虑异步操作控制,避免用异步的编程语法而偷运着“阻塞”之事,本身的操作就会更流程。 例如,通讯时,程序调用时通过一个原型声明为
void SendMessage<M,T>(Socket client, M message, Action<T> callback, Action tmeout);
的方法来发送消息,它发送消息之后(瞬间)就结束了当前线程了,然后当相同的 client 收到消息时会内存中事先注册、保存的数据中找到对应的callback 而再次触发回调事件。并且主线程可能每隔一段时间(例如1分钟)去清理一下超时未回复的注册数据,回调其 timeout 委托方法。如果你在通讯协议上有“顺序编号”字段,那么就算是发送方以顺序“1、2、3、4、5”连续发了5条指令,而接收方以“1、4、3、5、2”顺序返回的结果,这个 SendMessage 方法流程也能够正确地回调 callback或者是 timeout委托,而不会搞错乱了。 这里最为关键地是,它的流程不搞什么 _sendTimeoutEvent 这种东西!它不使用一个拖死、阻塞线程的策略去设计程序,它是真正异步的流程,而不是用异步操作编程语句去偷运同步顺序编程思路。实际上当发送消息时并不去“阻塞”线程,因此这是真异步,不是伪异步。
wszd0 2016-05-09
  • 打赏
  • 举报
回复
引用 5 楼 oysy 的回复:
这样的话,下面必须设置心跳包, 在规定的心跳包时间里,没有数据上来,就得断开哪个连接. 而且应该是下面直接发数据上来,怎么能让你去读呢?
对,是设备发上来的数据,先是_socket.send给设备发送请求,_socket.BeginReceive去读取设备发上来的数据
wszd0 2016-05-09
  • 打赏
  • 举报
回复
引用 2 楼 xian_wwq 的回复:
比较可靠的办法是, client如果没有业务数据发送,就发送心跳包 server侧对连接的client进行定期的检查 如果出现超时,则直接关闭socket
client端与server端没有出现过卡死,通讯正常。是server端采集设备数据的时候出现的卡死,也不是经常出现,偶尔会发生。
飞天凤凰601 2016-05-09
  • 打赏
  • 举报
回复
这样的话,下面必须设置心跳包, 在规定的心跳包时间里,没有数据上来,就得断开哪个连接. 而且应该是下面直接发数据上来,怎么能让你去读呢?
wszd0 2016-05-09
  • 打赏
  • 举报
回复
引用 1 楼 tcmakebest 的回复:
设备断电后, 数据接收会超时, 但你又调用 _socket.EndReceive 继续等待数据, 把超时功能给废了, 此时应该关闭连接.
您的意思是把这部分删掉吗 if (!_sendTimeoutEvent.WaitOne(timeout, false)) { _socket.EndReceive(iar); //Error = string.Format("超时: {0}", timeout); //log.ErrorFormat("< 超时: {0}", timeout); } 或者改成 if (!_sendTimeoutEvent.WaitOne(timeout, false)) { _socket..close(); //Error = string.Format("超时: {0}", timeout); //log.ErrorFormat("< 超时: {0}", timeout); }
yahle 2016-05-09
  • 打赏
  • 举报
回复
如果会有断网的情况,建议用楼上的心跳包来做检查,定时干掉没有响应的sokcet。
xian_wwq 2016-05-09
  • 打赏
  • 举报
回复
比较可靠的办法是, client如果没有业务数据发送,就发送心跳包 server侧对连接的client进行定期的检查 如果出现超时,则直接关闭socket
wszd0 2016-05-09
  • 打赏
  • 举报
回复
引用 12 楼 oysy 的回复:
刚才去看了一下,已经正常运行25天多了
我这个通讯的设备有自己的协议,必须先发个请求才给回复数据。问题出在收发数据的瞬间断网,造成卡死。我找到一些问题,改了一下,目前还没有发现异常。
wszd0 2016-05-09
  • 打赏
  • 举报
回复
引用 10 楼 sp1234 的回复:
甚至用异步编程语法去偷运着通讯顺序之事 --> 甚至用异步编程语法去偷运着同步顺序之事
感谢您的指点,您说的很专业,我是一个c#初学者,通过多位朋友的帮忙,已经找到解决的办法。这个程序是别人写的,设计思路可能是有点问题,以后多向您学习!
tcmakebest 2016-05-09
  • 打赏
  • 举报
回复
设备断电后, 数据接收会超时, 但你又调用 _socket.EndReceive 继续等待数据, 把超时功能给废了, 此时应该关闭连接.

111,129

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧