DotNet:SocketException (111):Debian 上的 UDP 连接被拒绝

DotNet: SocketException (111): Connection refused for UDP on Debian

我遇到了一个奇怪的问题;我有两个应用程序,一个前端和一个后端。前端使用 UDP 向后端发送消息。我有使用 UDP 的特定原因,但其中之一是我不在乎后端何时因某种原因关闭或无法访问。我只是想让消息丢失。是的,你没有看错

现在忘掉后端吧。让我们假设后端未启动,但前端无论如何都会向后端发送一个 UDP 数据包。客户端发送永远不会到达的消息,在 Windows 上就好了。如预期。在 Linux(确切地说是 Debian)上,我得到了 SocketException (111): Connection refused。当我将消息发送到(不存在的)远程主机时,不会发生这种情况。但是,一旦主机为 localhost127.0.0.1 甚至 192.168.1.1(机器的 LAN IP),就会抛出异常。当主机是 8.8.8.8192.168.1.10(我局域网中的某个随机主机)或 192.168.1.200(我局域网中不存在的主机)时,例外情况是 not 抛出。

因为我觉得我快疯了所以我用其他语言测试了它,它们似乎都表现正确(即发送消息(永远不会到达),没有例外)。

这是有效的,全部在同一台 Debian 10 机器上:

Python:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
    sock.sendto(b"Hello, World!", ("127.0.0.1", 12345))
    print(".", end = '')

PHP:

<?php
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);

$msg = "Hello world!";
$len = strlen($msg);

while (true) {
    socket_sendto($sock, $msg, $len, 0, '127.0.0.1', 12345);
    echo '.';
}

Perl:

use Socket;
socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!";

$MSG = "Hello world!";
$endpoint = sockaddr_in(12345, inet_aton("127.0.0.1"));
while(true) {
    send(SOCKET, $MSG, 0, $endpoint) == length($MSG) or die "cannot send: $!";
    print(".");
}

对于上述所有三个脚本,输出为(如预期):.............…

现在是 .Net (C#) 版本(我开始时是异步的,但在我的搜索中我什至回到了同步):

C# 同步:

static void Main(string[] args)
{
    var data = Encoding.UTF8.GetBytes("Hello world");
    var client = new UdpClient("127.0.0.1", 12345);

    while (true) {
        client.Send(data, data.Length);
        Console.Write(".");
    }
}

C# 异步:

static async Task Main(string[] args)
{
    var data = Encoding.UTF8.GetBytes("Hello world");
    var client = new UdpClient("127.0.0.1", 12345);

    while (true) {
        await client.SendAsync(data, data.Length).ConfigureAwait(false);
        Console.Write(".");
    }
}

我什至将代码重写为 而不是 使用 UdpClientSocket;同样的结果。 .Net 不断抛出 SocketException (111): Connection refused.

我唯一发现 可能 相关的是 this over at PerlMonks:

Snippet from socket man page SOCKET OPTIONS section for option SO_BSDCOMPAT:

If enabled ICMP errors received for a UDP socket will not be passed to the user program. In later kernel versions, support for this option has been phased out: Linux 2.4 silently ignores it, and Linux 2.6 generates a kernel warning (printk()) if a program uses this option.

但是因为我无法设置任何标志,甚至远程类似于 SO_BSDCOMPAT,这似乎是一个死胡同(而且可能根本不是问题)。

有人任何线索知道这里发生了什么吗?

总结:当没有“后端”(或服务器)侦听时,C# 以上在 Windows 上运行良好;消息只是丢失 - 正如预期的那样。在 Linux(Debian 10)上,C# 代码抛出异常,但 Python、Perl 和 PHP 似乎工作正常,排除了 OS(恕我直言)。我是 运行 dotnet 5.0.202、Windows 10 和 Debian 10(Linux myhost 4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64 GNU/Linux) - 都是最新的。

好吧,你一定是在开玩笑...正如我所说:

I even rewrote the code to not use a UdpClient but a Socket

...但我在套接字上使用了 .Send().SendAsync() 方法。我刚刚尝试使用 .SendTo().SendToAsync() 方法,这似乎有效。哪个...有点有意义,因为要使 .Send().SendAsync() 方法起作用,您需要先调用 .Connect() 并且 .SendTo()/.SendToAsync() 允许您在 SendTo... 调用本身中指定远程端点。

static void Main(string[] args)
{
    var data = Encoding.UTF8.GetBytes("Hello world");
    var socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
    var ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);

    while (true) {
        socket.SendTo(data, ep);
        Console.Write(".");
    }
}

所以这可以/将被标记为已解决。