奇怪的TCP连接方案

时间:2015-01-27 16:11:42

标签: c# tcp

我正在使用TCP作为保持活动的机制,这是我的代码:

客户端

TcpClient keepAliveTcpClient = new TcpClient();
keepAliveTcpClient.Connect(HostId, tcpPort);

//this 'read' is supposed to blocked till a legal disconnect is requested
//or till the server unexpectedly dissapears
int numberOfByptes = keepAliveTcpClient.GetStream().Read(new byte[10], 0, 10);

//more client code...

服务器

TcpListener _tcpListener = new TcpListener(IPAddress.Any, 1000);
_tcpListener.Start();
_tcpClient = _tcpListener.AcceptTcpClient();
Tracer.Write(Tracer.TraceLevel.INFO, "get a client");

buffer = new byte[10];
numOfBytes = _tcpClient.GetStream().Read(buffer, 0, buffer.Length);
if(numOfBytes==0)
{
    //shouldn't reach here unless the connection is close...
}

我只放了相关的代码......现在发生的是客户端代码是按预期读取的块,但服务器读取立即返回numOfBytes等于0 ,即使我重试在服务器上执行读取它立即返回...但客户端读取仍然是块!所以在服务器端我错误地认为客户端与服务器断开连接,但客户端认为它连接到服务器...有人可以告诉它是如何可能的?或者我的机制有什么问题?

编辑:失败后我写了这些属性的日志:

_tcpClient: _tcpClient.Connected = true

套接字:(_ tcpClient.Client属性)

_tcpClient.Client.Available=0
_tcpClient.Client.Blocking=true
_tcpClient.Client.Connected=true
_tcpClient.Client.IsBound=true

流细节

_tcpClient.GetStream().DataAvailable=false;

4 个答案:

答案 0 :(得分:2)

即使正确实施,此方法也只会检测一些远程服务器故障。考虑中间网络对两台机器进行分区的情况。然后,只有当底层TCP堆栈发送传输级别keep-alive时,系统才会检测到故障。 Keepalive是对问题的良好描述。 Does a TCP socket connection have a “keep alive”?是一个伴生问题。 RFC表示功能是可选的。

可靠地确认对方仍然活着的唯一确定方法是偶尔在两个端点之间发送实际数据。这将导致TCP及时检测到故障并将其报告给应用程序。

答案 1 :(得分:2)

  

也许会产生线索:只有10个或更多客户才会发生这种情况   同时连接服务器(服务器侦听10个或更多端口)。

如果您在Windows 7/8上编写此代码,则可能会遇到连接限制问题。微软的许可证允许20个并发连接,但措辞非常具体:

[开始 - >运行 - > winver,点击" Microsoft软件许可条款"]

  

3e中。设备连接。您可以允许最多20个其他设备访问许可计算机上安装的软件,以仅使用文件服务,打印服务,Internet信息服务和Internet连接共享和电话服务

由于您所做的事情不是文件,打印,IIS,ICS或电话,因此在这些情况下,XP / Vista的先前连接限制仍然可以实施10 。暂时将代码中的并发连接限制设置为9,并查看它是否一直发生。

答案 2 :(得分:2)

我解释MSDN评论的方式似乎是预期的行为。如果没有数据,则读取方法返回。

考虑到这一点,我认为我会尝试以指定的间隔发送数据,就像之前的一些建议一样,以及某种“超时”。如果在指定的时间间隔内没有看到“ping”,则可能会使keepalive失败。使用TCP,您必须记住,没有必要因为您没有看到数据而认为连接“已损坏”。您可以完全拔掉网络电缆,在您发送一些数据之前,连接仍然会被认为是好的。发送数据后,您将看到以下两种行为之一。要么你永远不会看到响应(监听机器被关闭?)或者你会得到一个“ack-reset”(监听机器不再监听那个特定的插座)

https://msdn.microsoft.com/en-us/library/vstudio/system.net.sockets.networkstream.read(v=vs.100).aspx

说明: 此方法将数据读入buffer参数并返回成功读取的字节数。 如果没有可用于读取的数据,则Read方法返回0. Read操作读取尽可能多的数据,最多为size参数指定的字节数。如果远程主机关闭连接,并且已收到所有可用数据,则Read方法立即完成并返回零字节。

答案 3 :(得分:-2)

我可以看到你正在阅读双方,服务器和客户端的数据。您需要将一些数据从服务器写入客户端,以确保您的客户端有一些东西可以阅读。你可以在下面找到一个小的测试程序(任务的东西只是在同一个程序中运行服务器和客户端)。

class Program
{
    private static Task _tcpServerTask;
    private const int ServerPort = 1000;

    static void Main(string[] args)
    {
        StartTcpServer();
        KeepAlive();

        Console.ReadKey();
    }

    private static void StartTcpServer()
    {
        _tcpServerTask = new Task(() =>
        {
            var tcpListener = new TcpListener(IPAddress.Any, ServerPort);
            tcpListener.Start();

            var tcpClient = tcpListener.AcceptTcpClient();
            Console.WriteLine("Server got client ...");

            using (var stream = tcpClient.GetStream())
            {
                const string message = "Stay alive!!!";

                var arrayMessage = Encoding.UTF8.GetBytes(message);
                stream.Write(arrayMessage, 0, arrayMessage.Length);   
            }

            tcpListener.Stop();
        });

        _tcpServerTask.Start();
    }

    private static void KeepAlive()
    {
        var tcpClient = new TcpClient();
        tcpClient.Connect("127.0.0.1", ServerPort);
        using (var stream = tcpClient.GetStream())
        {
            var buffer = new byte[16];
            while (stream.Read(buffer, 0, buffer.Length) != 0)
                Console.WriteLine("Client received: {0} ", Encoding.UTF8.GetString(buffer));
        }
    }
}