【问题标题】:UDP Multicast: Socket.SendTo is blockingUDP 多播:Socket.SendTo 阻塞
【发布时间】:2013-09-13 13:34:43
【问题描述】:

我有一个发送多播数据包的应用程序。

我使用 BlockingCollection 来存储我必须发送的消息,然后一个线程专门用于发送只是迭代这个集合并在其中发送消息。每个本地 IP 都有一个套接字,因为我需要在每个 IP 上发送它。

它通常工作得很好,但最近,我的线程有几次挂在 Socket.SendTo 调用上,导致我的应用程序不再响应任何外部调用。我的 BlockingCollection 越来越大,而且从未被处理过。

我找不到任何理由让这个 Socket.SendTo 调用阻塞我的调用。如果是 TCP 调用,我可以想象对方没有人,但在这里使用 UDP,我看不出任何可能的问题。

我的代码基本上是:

Debug.WriteLine("Sending message on local endpoint " + socket.Key + ", to " + remoteEndpoint);
int sendTo = socket.Value.SendTo(buffer, remoteEndpoint);
Debug.WriteLine("Successfully sent a packet with "+sendTo+" bytes");

在我的日志中我看到:

[...]
Sending message on local endpoint 10.10.23.56, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 10.9.53.38, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 127.0.0.1, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 10.10.23.56, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 10.9.53.38, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 127.0.0.1, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 10.10.23.56, to 224.0.0.253:5353
Successfully sent a packet with 778 bytes
Sending message on local endpoint 10.9.53.38, to 224.0.0.253:5353¨

我这样创建我的套接字:

Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
sendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
sendSocket.MulticastLoopback = true;
sendSocket.Bind(new IPEndPoint(localAddress, m_port));
sendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,new MulticastOption(m_multicastAddress, localAddress));

我看到here 在我发送完所有消息之前一直处于阻止状态,但是出于什么原因我的消息可能会被这样阻止?但是在这里,我的电话在这里被阻止了几分钟。

然后该怎么办?有时间在我的数据包未发送时处理 Socket 并尝试再次创建它吗?

编辑:我尝试对异步发送/接收做同样的事情,但更糟糕的是:我仍然在 EndReceiveTo 上被阻止,但此外,它阻止我终止进程(访问被拒绝,我是管理员,有重启:/)

编辑 在 xaxxon 回答之后,我把它放在发送消息之前,看看我是否有无法写入或错误的套接字:

List<Socket> socketWrite = socketToUse.Select(s => s.Value).ToList();
List<Socket> socketError = socketToUse.Select(s => s.Value).ToList();
Socket.Select(null, socketWrite, socketError, 1000);
Console.WriteLine("Writeable sockets: " + String.Join(", ", socketWrite.Select(s => s.LocalEndPoint.ToString())));
Console.WriteLine("Sockets in errors: " + String.Join(", ", socketError.Select(s => s.LocalEndPoint.ToString())));

But before I enter I get stuck  on my `SendTo` call, 
Writeable sockets: 10.10.23.56:5353, 10.9.53.38:5353, 127.0.0.1:5353
Sockets in errors:
Sending message on local endpoint 10.10.23.56, to 224.0.0.253:5353
Successfully sent a packet with 93 bytes
Sending message on local endpoint 10.9.53.38, to 224.0.0.253:5353
Successfully sent a packet with 93 bytes
Sending message on local endpoint 127.0.0.1, to 224.0.0.253:5353
Successfully sent a packet with 93 bytes
Writeable sockets: 10.10.23.56:5353, 10.9.53.38:5353, 127.0.0.1:5353
Sockets in errors:
Sending message on local endpoint 10.10.23.56, to 224.0.0.253:5353
Successfully sent a packet with 1162 bytes
Sending message on local endpoint 10.9.53.38, to 224.0.0.253:5353
__HERE I GET STUCK

【问题讨论】:

  • 你正在做环回,但你没有阅读它。如果您不打算阅读,请不要设置环回。
  • 但是,它是 UDP 多播,应该不需要阅读它们。另外我已经他们了(事实上,两次,服务器监听消息,客户端也是)。但这不是我当前的问题,因为我阻止了另一个 IP
  • 如果您不想阅读它们,为什么要启用环回?这没有意义。请注意,这些是 cmets 而不是答案。
  • 因为有时我需要拥有它们(比如在同一个工作站上拥有客户端和服务器时)。但同样,这不是我当前的问题

标签: c# .net sockets udp multicast


【解决方案1】:

与 TCP 套接字一样,即使是 UDP 套接字也有一个传出(发送)缓冲区。如果该发送缓冲区已满,那么即使 sendTo() 调用也会阻塞。您可以尝试设置 SO_SNDBUF 套接字选项以增加传出发送缓冲区的大小。另一个原因是您是否需要解析 ARP。如果路由中的下一跳或目的地本身位于同一子网中并且没有 ARP 条目,则可能会发生这种情况。由于 ARP 解析由 ARP 请求/ARP 响应组成,因此当您收到 ARP 响应时,发送方可能已经将足够的数据包排入队列以填充发送缓冲区。我会尝试逐步增加 SO_SNDBUF 并查看您遇到的问题是否变得不那么严重 - 这将有助于确定发送缓冲区确实是阻塞问题的原因。

这是一个关于设置 SO_SNDBUF 的链接。 http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476%28v=vs.85%29.aspx

【讨论】:

  • 我会尽快测试。这个缓冲区是基于数据包的数量吗?尺寸?我们是否知道填充这个传出缓冲区需要什么?因为我不觉得我填得很快。我有两个应用程序(客户端和服务器),用于测试,我在同一台计算机上运行(使用 REUSE_ADDRESS)。我认为这是客户端和服务器的通用缓冲区吗?但另一个客户没有问题。
  • @J4N 缓冲区基于字节数而不是数据包数。当我们有两个套接字时,两个套接字都有自己的发送缓冲区,即使它们绑定到同一个端口(使用重用地址选项)。
  • 弄乱缓冲区大小是非常糟糕的建议。您将永远无法预测您必须发送多少数据或操作系统将允许您做什么。非阻塞 I/O 是唯一可行的方法。
  • 总的来说,我同意你的评论。但是,只要您知道自己在做什么,就可以根据这些选项调整套接字——毕竟,这就是套接字层提供这些选项的原因。我假设应用程序不想采用非阻塞方式,因为这样应用程序必须确定一旦出现 EWOULDBLOCK/EAGAIN 错误,何时重试。
  • 问题是我永远不能确定它会一直有效。我想我会使用这个解决方案来增加非阻塞发送消息,但我仍然需要一种方法来检测和解决这个问题。
【解决方案2】:

不要乱用套接字发送缓冲区。不可能在所有情况下都做到这一点。

改为使用非阻塞套接字,并在发送前检查套接字是否可写。当有可用空间时,这将让您填满缓冲区。

【讨论】:

  • 谢谢。如何检查套接字是否可写?如果我看到它不可写,那么正确的行为是什么?我的意思是,我需要发送消息,所以等待缓冲区可写与等待缓冲区没有相同的效果?非阻塞套接字 = 异步套接字?由于这个线程唯一的工作就是发送数据包,如果我用异步方式做,它会直接尝试发送下一个,我不确定这是不是好的解决方案?
  • 我不是 c# 人,但 select 是您正在寻找的大多数语言中的函数。
  • 选择?我看不到任何方法可以对我的套接字对象进行此调用:/
  • 对不起,我正在检查会员。但是,当检测到套接字缓冲区已满时,什么是好的行为呢?因为我需要发送所有消息?
  • 我添加了我的帖子的“编辑”以向您显示结果,我不确定我为什么会被阻止?
猜你喜欢
  • 2015-08-06
  • 2017-02-26
  • 1970-01-01
  • 2013-12-10
  • 2017-04-07
  • 2016-10-08
  • 2015-06-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多