【问题标题】:C# TCPClient/Socket writing not throwing exceptionC# TCPClient/Socket 编写不抛出异常
【发布时间】:2018-06-25 23:04:50
【问题描述】:

我尝试通过 tcp 连接连接许多打印机。我正在尝试验证我的 TcpClient 是否仍然连接以更新 GUI。我正在尝试写入套接字以确保其仍然连接。即使拔掉电缆我也不例外我在这里尝试了所有建议MSDN_Fourm

我在尝试检查打印机状态后收到预期的异常

伪代码 client是之前已经连接过的TCPClient

private bool FuntionPsudo(){
    try{
        if(client.Connected){
            byte[] buf = new byte[1];
            client.Client.Send(buf, 0,0);
            client.GetStream().Write(buf,0,0);
            if(client.Client.Receive(buf,SocketFlags.Peek)==0)
                return false;
            return true;
        }
    }
    catch(Exception){
        return false;
    }

    return false;
}

FuntionPsudo 返回:true

电缆已拔下

FuntionPsudo 返回:true

FuntionPsudo 返回:true

检查打印机状态

FuntionPsudo 返回:false

提前感谢您提供有关为什么会发生这种情况和/或如何解决它的任何帮助

【问题讨论】:

  • 拔下电缆不会自动关闭现有连接,因为该连接的任何一方都不关心/知道物理状态。这就是超时的用途。
  • 好的,但是当我写入套接字时,它不应该抛出异常吗?
  • 除非连接/套接字实际上已关闭,否则不会。如果您手动关闭套接字,或者等待很长时间以使连接超时,是否会引发异常?
  • 如果我使用client.Close(); 我确实得到了异常,有没有办法判断套接字是否仍然连接?

标签: c# sockets tcp tcpclient


【解决方案1】:

经过几次失败的尝试,我意识到“拔下电缆”类型的连接检测并不那么容易。同时我发现有一些技巧可以用来检查服务器是否关闭了连接,所有这些都不需要发送 heartbeat 类型的消息。

这是我想出的,我可以说它在大多数情况下都有效(尤其是在电缆断开时,很难确定连接是否仍然正常)

static class SocketUtils
{
    public static bool IsConnected(this Socket socket)
    {
        return IsSocketConnected(socket) && IsNetworkConnected(socket);
    }

    public static void KeepAlive(this Socket socket, int pollSeconds)
    {
        socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
        SetIOControlKeepAlive(socket, (uint)(pollSeconds * 1000), 1);
    }

    static bool IsNetworkConnected(this Socket socket)
    {
        try
        {
            return socket.Send(new byte[0]) == 0;
        }
        catch (SocketException) { return false; }
    }

    static bool IsSocketConnected(this Socket socket)
    {
        try
        {
            return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
        }
        catch (SocketException) { return false; }
    }

    static void SetIOControlKeepAlive(Socket socket, uint time, uint interval)
    {
        var sizeOfUint = Marshal.SizeOf(time);
        var inOptionValues = new byte[sizeOfUint * 3];
        BitConverter.GetBytes((uint)(time == 0 ? 0UL : 1UL)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes(time).CopyTo(inOptionValues, sizeOfUint);
        BitConverter.GetBytes(interval).CopyTo(inOptionValues, sizeOfUint * 2);

        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }
}

这里是你如何使用它:

var tcpClient = new TcpClient();

tcpClient.Connect("192.168.2.20", 3000);

// set this to a low value to detect cable disconnects early
tcpClient.Client.KeepAlive(30); // 30 seconds

Console.WriteLine("Connected..");
while (true)
{
    Thread.Sleep(500);
    Console.WriteLine(tcpClient.Client.IsConnected());
}

我必须补充一点,我厚颜无耻地从 Samuel's answer about checking client disconnectsGreg Dean's answer about setting keep-alive on the socket 复制了一些代码,所以也应该归功于他们 ;)

【讨论】:

  • @user2144406 结合上面的代码?还是以前试过?
  • 我按照您的建议尝试了client.NoDelay = true;,但并没有改变这种情况
  • 是的,我禁用了发送缓冲区,但没有帮助
  • @user2144406 请告诉我您是否尝试更改超时和调整保活。
  • @user2144406 不得不重写整个事情。看看这是否适合你。恐怕这类问题似乎没有 100% 的解决方案
【解决方案2】:

您只能通过发送和接收返回来判断您是否仍然连接。即使它们进入黑洞,只需将字节推送到网络中也总是有效的。 Connected 属性不可靠,几乎所有使用它的代码都是错误的。

向打印机发送一些东西并收到回复。或者,创建一个新连接(内部将发送和接收没有数据的 TCP 控制数据包)。

【讨论】:

  • 创建一个新连接确实有效,但是需要很长时间,所以只是澄清一下,如果不建立新连接就无法知道它是否仍然连接?你是这么说的吗?写和响应代码不是在发送和接收东西吗?
  • 是的,connected 属性是不可靠的(谢谢!)但是发送一个空消息实际上应该通过网络发送一个 TCP 数据包(这本质上就是 keep-alive)并且 应该 更新套接字状态。
  • 为什么当你发出空写时 TCP 堆栈会发送任何东西?浪费性能。另一方认为不需要确认零字节。没有意义。当您发出发送时,您不会生成数据包。您正在排队等待在堆栈空闲时发送的字节。您必须通过发送和接收数据来强制确认。
  • 确定如何“强制确认”?
  • 我还能告诉你什么?通过发送和接收某些东西。抱歉,如果这不是您想听到的。
【解决方案3】:

在处理 TCP 协议等传输层时,您需要像“对讲机”一样使用它。你需要决定什么时候和多久说话。换句话说,当双方同时说话或收听时,沟通就会中断。

以下是《C# in a Nutshell》一书中的一个示例:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class TcpDemo
{
    static void Main()
    {
        new Thread(Server).Start();     // Run server method concurrently.
        Thread.Sleep(500);              // Give server time to start.
        Client();           
    }

    static void Client()
    {
        using (TcpClient client = new TcpClient("localhost", 51111 ))
        using(NetworkStream n = client.GetStream())
        {
            BinaryWriter w = new BinaryWriter(n);
            w.Write("Hello");
            w.Flush();
            Console.WriteLine(new BinaryReader(n).ReadString());
        }
    }

    static void Server()
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 51111);
        listener.Start();
        using(TcpClient c = listener.AcceptTcpClient())
        using(NetworkStream n = c.GetStream())
        {
            string msg = new BinaryReader(n).ReadString();
            BinaryWriter w = new BinaryWriter(n);
            w.Write(msg + " right back!");
            w.Flush();
        }
        listener.Stop();
    }
}

【讨论】:

  • 好的,我明白了你的前提,但不知道它是如何真正适用的,也许你可以再解释一下
  • 你必须遵循这个顺序。 1.打开TcpClient; 2.写; 3.关闭; 4.打开TcpLister; 5.阅读; 6. 关闭。如果你打开 TcpClient 然后你写然后你听是行不通的。在示例代码中,“使用”负责关闭,这是您看不到关闭的方式。
【解决方案4】:

我有相同的重新连接问题。 我用java编写服务器,用c#编写客户端(统一) java-java抛出异常ok java-c# : 在某些情况下它们都会抛出异常。

我有性能服务器的最佳方式 我顺便解决了:编写jar客户端并使用ikvm导出到dll(将jar复制到ikvm/bin)。在 c# 中创建库并引用 dll + ikvm.core.dll + .../manage/unityEngine.dll ==> 将 Cshapr/bin/Debug 复制到 UnityProJect/Asset --> 运行正常,但构建速度超过 37M ?

如果你想拥有一个小型客户端 --> 无需重新连接的网络?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多