【问题标题】:Weird tcp connection scenario奇怪的 tcp 连接场景
【发布时间】:2015-03-26 07:54:29
【问题描述】:

我正在使用 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;

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 什么buffer.Length?
  • @usr 我添加了缓冲区初始化...它的大小是 10
  • 好的,这在正常情况下是不会发生的。打破常规思考问题。您是否连接到正确的端点?杀死服务器以确保。您是否意外关闭了 _tcpClient?顺便说一句,TCP keepalive 不能用于快速或可靠地检测断开连接。也许整个问题都没有实际意义。
  • 我告诉你,这种机制大部分时间都有效,但 20 个连接中有 1 个的行为与我上面描述的一样。我确信我连接到正确的端点,我可以在日志中看到它,并且我确信我不会意外关闭 _tcpClient。这真是奇怪的行为,这就是我问这个问题的原因......也许有人有类似的经历......

标签: c# tcp


【解决方案1】:

即使正确实施,这种方法也只会检测到一些远程服务器故障。考虑中间网络对两台机器进行分区的情况。然后,只有当底层 TCP 栈发送传输层 keep-alive 时,系统才会检测到故障。 Keepalive 很好地描述了这个问题。 [TCP 套接字连接是否有“保持活动状态”?]2 是一个配套问题。 RFC 表示该功能是可选的。

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

【讨论】:

    【解决方案2】:

    也许可以提供线索:仅当有 10 个或更多客户时才会发生 同时连接服务器(服务器监听10个或更多端口)。

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

    [开始->运行->winver,点击“微软软件许可条款”]

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

    由于您所做的不是文件、打印、IIS、ICS 或电话,因此在这些情况下可能仍然强制执行 XP/Vista 之前的 10 连接限制。在您的代码中暂时将并发连接数限制为 9,然后查看它是否继续发生。

    【讨论】:

      【解决方案3】:

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

      考虑到这一点,我想我会尝试以指定的时间间隔发送数据,就像前面的一些建议一样,以及某种“超时”。如果您在指定的时间间隔内没有看到“ping”,您可能无法保持连接。使用 TCP,您必须记住,没有要求仅仅因为您没有看到数据就认为连接“断开”。您可以完全拔下网络电缆,并且在您发送一些数据之前,连接仍然被认为是良好的。发送数据后,您将看到以下两种行为之一。要么你永远看不到响应(监听机器已关闭?),要么你会得到一个“ack-reset”(监听机器不再监听那个特定的套接字)

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

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

      【讨论】:

        【解决方案4】:

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

        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));
                }
            }
        }
        

        【讨论】:

        • 在我的机制中,客户端每 10 秒向服务器发送一次“a”以确保 TCP 连接仍然存在,因此服务器循环运行并检查每次是否收到“a”。
        • “您需要将一些数据从服务器写入客户端,以确保您的客户端有要读取的内容”——如果 OP 希望客户端读取实际数据,这可能是正确的。但是Read() 方法的返回值 0 意味着一些非常具体的东西(远程端点已经关闭了套接字),而不是由于数据本身的缺失。所以向套接字写入数据是没有用的。
        猜你喜欢
        • 2021-05-22
        • 1970-01-01
        • 2017-11-04
        • 2015-06-17
        • 2012-07-15
        • 1970-01-01
        • 2023-02-07
        • 2019-01-08
        • 1970-01-01
        相关资源
        最近更新 更多