最近做socket保持长连接的一些心得,欢迎大家讨论

cqsfd 2010-01-21 05:56:46
自己写的客户端马上要发布了,忽然发现了一大堆问题,主要集中在与服务器的TCP连接经常莫名断开,客户端又检测不到,不能及时重连。一个多星期的修改,有一些心得,与大家分享。也希望大家多发表意见,您的意见也许最后就实现在我的软件中了!

主要分为两部分:
一,如何更好的检测TCP连接是否正常
二,如何提取本机TCP连接状态

一,如何更好的检测TCP连接是否正常
这方面问题,我上网查了很久,一般来说比较成熟的有两种方法:
1是在应用层制定协议,发心跳包,这也是C#,JAVA等高级语言比较常用的方法。客户端和服务端制定一个通讯协议,每隔一定时间(一般15秒左右),由一方发起,向对方发送协议包;对方收到这个包后,按指定好的通讯协议回一个。若没收到回复,则判断网络出现问题,服务器可及时的断开连接,客户端也可以及时重连。
2通过TCP协议层发送KeepAlive包。这个方法只需设置好你使用的TCP的KeepAlive项就好,其他的操作系统会帮你完成。操作系统会按时发送KeepAlive包,一发现网络异常,马上断开。我就是使用这个方法,也是重点向大家介绍的。

使用第二种方法的好处,是我们在应用层不需自己定协议,通信的两端,只要有一端设好这个值,两边都能及时检测出TCP连接情况。而且这些都是操作系统帮你自动完成的。像我们公司的服务端代码就是早写好的,很难改动。以前也没加入心跳机制,后面要改很麻烦,boss要求检测连接的工作尽量客户端单独完成....
还有一个好处就是节省网络资源。KeepAlive包,只有很简单的一些TCP信息,无论如何也是比你自己设计的心跳包短小的。然后就是它的发送机制,在TCP空闲XXX秒后才开始发送。自己设计心跳机制的话,很难做到这一点。

这种方法也是有些缺陷的。比如某一时刻,网线松了,如果刚好被KeepAlive包检测到,它会马上断开TCP连接。但其实这时候TCP连接也算是established的,只要网线再插好,这个连接还是可以正常工作的。这种情况,大家有什么好办法处理吗?

C#中设置KeepAlive的代码
uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)15000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)15000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);

IPEndPoint iep = new IPEndPoint(this._IPadd, xxxx);
this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this._socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
this._socket.Connect(iep);
这里我设定TCP15秒钟空闲,就开始发送KeepAlive包,其实完全可是设定得长一点。


二,如何提取本机TCP连接状态
设好了KeepAlive值,又遇到麻烦了,我没找到当网络异常时,它断开连接后怎么通知我...我搜了很久都没找到,要是哪位兄弟知道的话告诉我吧。我是使用笨办法的,找到所有本地TCP连接的信息,筛选出我需要的那个TCP。
查看本机所有TCP连接信息,网上一般的方法,都是通过程序调用CMD命令里的netstat进行,然后再分析其内容。但在CMD窗口用过这个命令的都知道,悲剧的时候它显示完所有TCP信息需要15s,或者更长时间,这在我的程序中是不能忍受的。
然后我又查找了一些牛人的博客,发现有人提到用iphlpapi.dll。这是一个在win98以上操作系统目录System32都包含的库函数,功能异常强大,大家可以放心使用!但是使用起来比较麻烦,基本找不到C#现成使用的例子,就算有,也是很老版本的,完全不能用
我参考了这两位高人的博客
http://blog.csdn.net/yulinlover/archive/2009/02/08/3868824.aspx
(另一位的博客连接找不到了..悲剧啊!)
下载了里面提到的项目,仔细结合自己体会进行修改,终于能用了。每隔一段时间,我的客户端就用这个方法扫描一遍本地TCP信息,若发现连接有问题,则断开重连。
这个方法能瞬间得到本机所有TCP连接信息(如果你有兴趣可以扩充,它的功能真的是太强大了),没有CMD命令netstat那不能忍受的延迟,相当好用。代码比较长,就不贴出来了。


这些是我不太成熟的做法,下星期项目就要提交了,不能再出啥岔子,希望大家多提意见,帮我改善一下。
本版人气很旺,但貌似用socket的人不多,不知道帖子发这是否合适。要是不合适,请前辈提点下发在哪个版比较好?
...全文
25436 99 打赏 收藏 转发到动态 举报
写回复
用AI写文章
99 条回复
切换为时间正序
请发表友善的回复…
发表回复
swift19221 2011-09-09
  • 打赏
  • 举报
回复
搞c++的路过
ILoveProgramer 2011-09-02
  • 打赏
  • 举报
回复
good
sanmao3 2011-06-02
  • 打赏
  • 举报
回复
这个贴很不错。。。
davidyang_cn 2010-11-22
  • 打赏
  • 举报
回复
学到很多东西,顶先
淡淡的琉璃 2010-09-29
  • 打赏
  • 举报
回复
都是c#的代码,有没有c的哦
mayang_lang 2010-09-09
  • 打赏
  • 举报
回复
长见识了,留个印,有需要还能再看。
xumin163 2010-08-30
  • 打赏
  • 举报
回复
[Quote=引用 32 楼 cyl_java 的回复:]
引用 31 楼 cqsfd 的回复:
引用 30 楼 cyl_java 的回复:
切,什么叫比较可行,本来这才是正确的做法

我多研究下有啥潜在问题

MM你也太搞了private voidSetXinTiao(Socket tmpsock)
我英语也很烂

这个是我们技术总监教我们的,当发现英文命名太长时不如用简短的拼音,命名没有什么死板的格式,要的就是简短易懂
[/Quote]

HeartBreak 太长吗?
jieon 2010-05-25
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 cyl_java 的回复:]
C# code

public AsyncSocket(Socket sock,int index)
{
this.index = index;
this.sock = sock;
this.SetXinTiao(this.sock);
Socket obj_Socket……
[/Quote]
好用
yinghuigu 2010-05-17
  • 打赏
  • 举报
回复
26楼的解决方法怎么看不到啊
hangang7403 2010-01-22
  • 打赏
  • 举报
回复
UP
haha0369 2010-01-22
  • 打赏
  • 举报
回复
先收藏,晚上回家看
Charles雨林 2010-01-22
  • 打赏
  • 举报
回复
[Quote=引用 53 楼 cqsfd 的回复:]
悲剧,是不是我的问题没提好,大家没理解呢?

还有26楼MM的代码,应该不是通过收到没内容的包判断网络异常的,是靠接收超时来判断异常的吧?
也请仔细说说你的判断思路吧,我怎么没看到设置超时的代码?
[/Quote]
跟超时无关,是底层抛出的异常。看来你是没写过异步的
Charles雨林 2010-01-22
  • 打赏
  • 举报
回复
[Quote=引用 47 楼 cqsfd 的回复:]
引用 45 楼 red_angelx 的回复:
private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                lock (this)
                {
                    int recvCount = _client.GetStream().EndRead(ar);

//这种情况就是断开了连接
                    if (recvCount  < 1)
                    {
                        if (OnSocketError != null)
                        {
                            _client.Close();
                            OnSocketError(this, new EventArgs());
                        }
                    }

这段代码刚好有我关心的很多问题

首先说明一下,在我的客户端里面,是采用同步发送的通信方式,
一个线程一值
while()


处理消息

因为通信在我这是非常重要的,可以说是主线程了,所有不可能像26楼MM那样改成异步通信的形式,如果仅仅加一个异步接受返回的KeepAlive包,就会和我主通信线程里的消息冲突

我对TCP的理解,不知道是不是有偏差,说错大家帮我更正。
就像45楼的代码,是在异步等待TCP的包。如果收到一个包,但里面没内容,就判断TCP断开,是这么理解这段程序吧?
但TCP其实有很多时候是在自己发送一下TCP层的,在应用层看来没内容的包,比如我说的这个KeepAlive包。像45L代码,会不会接受到一个试探网络的KeepAlive包就判断是网络出故障,主动断开呢?一般的系统,如果没有设置KeepAlive值,默认是2个小时的,就是说2个小时Tcp没有流量,就发一个这种包,red_angelx兄,你们的TCP连接能保持2个小时以上没流量又不断开吗?
而且有时候网络出故障,你不一定能收到这种没内容的包,这时候不就判断不出来了?
要是45L的代码没有任何问题,就证明我对TCP的理解有重大错误...恳请red_angelx指证

[/Quote]
“如果仅仅加一个异步接受返回的KeepAlive包,就会和我主通信线程里的消息冲突”
看了你的回答,我只能说楼主对KeepAlive并没有完全理解,KeepALive包并不会到应用层,它只是在监测到网络断线时向应用层抛出异常,那它会把异常抛到哪里呢?就是Receive、BeginReceive、EndReceive这些接收数据的地方。请问楼主是采用的阻塞模式吗?用多线程加上非阻塞模式也就是异步了,看来楼主对同步和异步也没搞清楚。这个贴早该结束了,从上面所有人的回答来看楼主早该找到答案了,还没找到答案是你自己有问题。说话狠了点,还请楼主多多包涵
xiongzhiqiang123 2010-01-22
  • 打赏
  • 举报
回复
学习
BATTLERxANGE 2010-01-22
  • 打赏
  • 举报
回复
学习,但我还是有一些其他的问题。
比如说用SOCKET传输数据的时候,如何考虑边界沾包等问题?尤其是在传输一些STRUCT,表,DATASET这些数据的时候,该如何处理这些问题?如果是SREING的话还好说呢,呵呵。
灵雨飘零 2010-01-22
  • 打赏
  • 举报
回复
up
cqsfd 2010-01-22
  • 打赏
  • 举报
回复
[Quote=引用 54 楼 lianshaohua 的回复:]
楼主太偷懒了。。。保持连接是应用层做的事情,最好不要交给C#的底层去处理,自己用心跳维持长连接最好。。。
[/Quote]
哥哥诶 不是我偷懒 是条件不允许我这么做
服务器那边不改 广我客户端发应用层的包有啥用啊
我一开始改的时候 就是发应用层的心跳包 程序都写好了
但服务器收到不是原来通讯协议的包都把TCP断掉

而且让底层去做,确实有很多优势,服务器那边也可以马上检测到网络异常,我在帖子里也说明了。如果我这方法行的通,应该是目前最好的解决方法了。到时候总结出来给大家看看
吃饭去,各位高手帮忙瞧瞧,不要吝啬留下您的意见
Lee 2010-01-22
  • 打赏
  • 举报
回复
学习........
ztenv 2010-01-22
  • 打赏
  • 举报
回复
楼主太偷懒了。。。保持连接是应用层做的事情,最好不要交给C#的底层去处理,自己用心跳维持长连接最好。。。
cqsfd 2010-01-22
  • 打赏
  • 举报
回复
悲剧,是不是我的问题没提好,大家没理解呢?

还有26楼MM的代码,应该不是通过收到没内容的包判断网络异常的,是靠接收超时来判断异常的吧?
也请仔细说说你的判断思路吧,我怎么没看到设置超时的代码?
加载更多回复(78)

110,545

社区成员

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

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

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