C#---Socket.Poll()

在服务器端,如何判断客户端的一个连接是否断开?
查找相关资料,得出较好的解决方案是使用socket对象的poll函数。

poll函数分析
下面是poll函数的官方描述:

public bool Poll (int microSeconds, System.Net.Sockets.SelectMode mode);
1
这是一个确定socket状态的函数。

参数
microSeconds Int32
等待响应的时间(以微妙为单位)。
mode SelectMode
SelectMode枚举类型中的一个,可选类型为SelectRead,SelectWrite,SelectError

返回值
Boolean 基于mode参数中传递的轮询模式值的socket的状态。

问题产生
首先,我的问题是从服务器端获得客户端的某个连接是否断开,作为服务器端,是需要对socketClient进行read操作的,因此对于poll函数中的第二个参数,选择SelectRead,这点毋庸置疑。
我们来看下,关于SelectRead模式的返回值的官方文档解析:
SelectRead:    如果已调用 Listen(Int32) 并且有挂起的连接,则为 true。
- 或 - 如果有数据可供读取,则为 true。 
- - 或 - 如果连接已关闭、重置或终止,则返回 true;
-  否则,返回 false。
对于该问题,网上的常规解决方法是:
使用Socket类中的Poll方法,就可以。
Socket client //假如已经创建好了,连接到服务器端得Socket的客户端对象。
我们只要client.Poll(10,SelectMode.SelectRead)判断就行了。只要返回True是。就可以认为客户端已经断开了。
对于上述的解决方法,我存在质疑,即返回为True的情况明明有三种,为什么只要是True就代表是连接已关闭、重置或终止呢?
问题解决
基于网上常规的解决方案,我进行了分析和实验,代码和结果如下:
while (true)
            {
                if (tcpClientSocket.Poll(10, SelectMode.SelectRead))
                {
                     Console.WriteLine("进入了if条件");
                     break;
                }
                int length = tcpClientSocket.Receive(data);
                Console.WriteLine(" 接受到的信息是: " + Encoding.UTF8.GetString(data, 0, length));
            }
这是简单的服务器接收客户端发送过来消息的代码。(客户端发消息的代码就不贴了,用个死循环send就好了)。
经过测试,发现if条件是能进去的,也就是说,poll返回True的时候,并不代表是连接断开了,也有可能是存在可读的数据。那么,网上的解决方法难道不对吗?
2. 其实,上面的方法用来判断连接断开也是可以的,并不完全不对,只是适用面窄了点。它有两个关键问题没有解决。
一、它只适用于客户端发送消息具有相对较长间隔的情况。也就是说,在大于poll函数响应时间间隔的情况下,send消息,那么就不会进入if循环了。因为,poll函数的第一个参数为响应时间,在这个响应时间内,相当于执行到poll这里,进程被“阻塞”了,在此时间内若接受到消息,或连接断开,那么poll返回的确实是True。所以在客户端中不停的使用while循环send消息,前面send的消息客户端receive还没有接收掉,相当于是存在“可读的数据了”,所以poll返回了True。
综上,想要使用上述模式,需要把响应时间设置小点;并且,客户端发送消息不能太快,至少要大于响应时间+系统receive的时间以保证不存在“可读的数据”。这样一来,每次执行上述的while循环时,可以确定,socket中已经不存在“可读的数据”了,数据都已经在上一次循环中被receive接受。那么进入if条件只有一种可能:连接断开。
二、上述模式不适用于连接错误断开的情况。究其原因,还是因为响应时间太短,10微妙的响应时间,导致基本上程序都是“阻塞”在int length = tcpClientSocket.Receive(data);这句代码上。此时若连接正常关闭(指使用socket.shutdown(both);socket.close()来关闭客户端),那么receive将收到0个字节的数据并输出,循环将在下一次的if中跳出;若不正常关闭(如控制台程序强制关闭等),那么receive将抛出异常,程序中断。我不想多输出这个0字节的数据,并想处理不管是正常或不正常的关闭,所以使用了如下的改进模式。

优化代码
 
            while (true)
            {

                if (tcpClientSocket.Poll(-1, SelectMode.SelectRead))
                {
                    try
                    {
                       int length = tcpClientSocket.Receive(data);
                        if (length == 0)
                        {
                            Console.WriteLine("连接正常断开!!");
                            break;
                        }
                        Console.WriteLine(" 接受到的信息是: " + Encoding.UTF8.GetString(data, 0, length));
                    }
                    catch
                    {
                        Console.WriteLine("连接异常断开!!");
                        break;
                    }
                }
            }    

将poll的第一个参数改为负数,那么程序运行到此处一定会阻塞,直到有可读的内容,或者连接断开。
能进入if循环,说明有三种情况:连接没断开,有数据需要接收或连接正常断开或连接异常断开。
对读取长度length进行判断,能确定正常断开情况;若异常断开,则通过try receive方法异常,来确定。
 

  • 7
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值