问题描述
我遇到了一个奇怪的问题;我有两个应用程序,一个前端和一个后端。前端使用 UDP 向后端发送消息。我有使用 UDP 的特定原因,但其中之一是我不在乎后端何时因某种原因关闭或无法访问。我只是想让消息丢失。是的,你没看错?
现在忘记后端。让我们播放后端未启动并且前端无论如何都会向后端发送 UDP 数据包的场景。客户端发送永远不会到达的消息,在 Windows 上就好了。如预期。在 Linux(确切地说是 Debian)上,我得到一个 SocketException (111): Connection refused
。当我将消息发送到(不存在的)远程主机时,不会发生。但是,只要主机是 localhost
或 127.0.0.1
甚至 192.168.1.1
(机器的 LAN IP),就会抛出异常。当主机是 8.8.8.8
或 192.168.1.10
(我的 LAN 中的某个随机主机)或 192.168.1.200
(我的 LAN 中不存在的主机)时,例外是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,'127.0.0.1',12345);
echo '.';
}
Perl:
use Socket;
socket(SOCKET,PF_INET,getprotobyname("udp")) or die "socket: $!";
$MSG = "Hello World!";
$endpoint = sockaddr_in(12345,inet_aton("127.0.0.1"));
while(true) {
send(SOCKET,$MSG,$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(".");
}
}
我什至重写了代码,不使用UdpClient
,而是使用Socket
;同样的结果。 .Net 不断抛出 SocketException (111): Connection refused
。
我发现可能唯一相关的是this over at PerlMonks:
选项 SO_BSDCOMPAT 的套接字手册页 SOCKET OPTIONS 部分的片段:
如果启用了为 UDP 套接字接收的 ICMP 错误,则不会将其传递给用户程序。在以后的内核版本中,对这个选项的支持已经被逐步淘汰:Linux 2.4 会默默地忽略它,如果程序使用这个选项,Linux 2.6 会生成一个内核警告 (printk())。
但由于我无法设置任何标志,甚至无法设置类似于 SO_BSDCOMPAT
之类的标志,这似乎是一个死胡同(甚至可能不是问题所在)。
有没有人ANY知道这里发生了什么?
总结:当没有“后端”(或服务器)监听时,以上 C# 在 Windows 上运行良好;消息会丢失 - 正如预期的那样。在 Linux (Debian 10) 上,C# 代码抛出异常,但 Python、Perl 和 PHP 似乎工作得很好,排除了操作系统 (恕我直言)。我正在运行 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)
- 都是最新的。
解决方法
好吧,你一定是在开玩笑...正如我所说:
我什至重写了代码以不使用 UdpClient
而是使用 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(".");
}
}
所以这可以/将被标记为已解决。