【发布时间】:2019-09-19 20:53:56
【问题描述】:
所以我一直很难找到关于确切当你有 2 个套接字应该如何表现的文档,它们都绑定到同一个端点,但其中一个也是 已连接 到远程端点。
- 套接字是 UDP IPv4
- 在 .net core 2.2/3 on linux x64 上运行
我能够从各种来源收集到的是,连接的套接字应该始终并且只从它所连接的端点接收数据报,而“未连接”的套接字将接收 其他一切。
我依稀记得读过内核套接字实现在 dgram 到达时为每个套接字分配“点”,并且得分较高的套接字(最具体的路由)获取数据。如果两个套接字获得相同的分数,则 dgram 在套接字之间是“负载平衡”的。
我做了一个小测试:
class Program
{
static void Main(string[] args)
{
var localEp = new IPEndPoint(IPAddress.Loopback, 1114);
var remoteEp = new IPEndPoint(IPAddress.Loopback, 1115);
//Socket bound to local EP, not connected should receive from everyone
var notConnected = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
notConnected.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
notConnected.Bind(localEp);
//Socket bound and connected should receive from only it's remote EP
var connected = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
connected.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
connected.Bind(localEp);
connected.Connect(remoteEp);
var notConnectedTask = Task.Run(() => Receive(notConnected, "Not Connected"));
var connectedTask = Task.Run(() => Receive(connected, "Connected"));
//Remote socket to send to connected socket
var remote1 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
remote1.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
remote1.Bind(remoteEp);
remote1.Connect(localEp);
//Remote socket to send to notConnected socket
var remote2 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
remote2.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
remote2.Bind(new IPEndPoint(IPAddress.Loopback, 1116));
remote2.Connect(localEp);
for (int i = 0; i < 10; i++)
{
//This should be received by connected socket only
remote1.Send(Encoding.Default.GetBytes($"message {i} to connected socket"));
//This should be received by unconnected socket only
remote2.Send(Encoding.Default.GetBytes($"message {i} to notConnected socket"));
}
remote1.Send(Encoding.Default.GetBytes("end"));
remote2.Send(Encoding.Default.GetBytes("end"));
Task.WaitAll(notConnectedTask, connectedTask);
}
public static void Receive(Socket sock, string name)
{
EndPoint ep = new IPEndPoint(IPAddress.Any, 0);
var buf = new byte[1024];
Console.WriteLine($"{name} is listening...");
while (true)
{
var rcvd = sock.ReceiveFrom(buf, ref ep);
var msg = Encoding.Default.GetString(buf.Take(rcvd).ToArray());
Console.WriteLine($"{name} => {msg}");
if (msg.SequenceEqual("end"))
return;
}
}
}
令我惊讶和懊恼的是,结果没有接近我的预期:
Connected is listening...
Not Connected is listening...
Connected => message 0 to connected socket
Connected => message 0 to notConnected socket
Connected => message 1 to connected socket
Connected => message 1 to notConnected socket
Connected => message 2 to connected socket
Connected => message 2 to notConnected socket
Connected => message 3 to connected socket
Connected => message 3 to notConnected socket
Connected => message 4 to connected socket
Connected => message 4 to notConnected socket
Connected => message 5 to connected socket
Connected => message 5 to notConnected socket
Connected => message 6 to connected socket
Connected => message 6 to notConnected socket
Connected => message 7 to connected socket
Connected => message 7 to notConnected socket
Connected => message 8 to connected socket
Connected => message 8 to notConnected socket
Connected => message 9 to connected socket
Connected => message 9 to notConnected socket
Connected => end
notConnected 套接字不仅没有收到任何东西,而且连接的套接字也得到了一切...
所以我的期望似乎都不是真的。没有负载均衡,也没有积分系统。
我有posted a comment SO 问了这个问题,得到的答复几乎证实了我的期望:
我认为如果一个连接到远程端点,那么所有数据报 源自该远程端点将最终在连接 插座。未连接的只会捕获来自其他的数据报 远程端点。
我还收到一封来自 OpenSSL 邮件组的电子邮件,其中大部分也确认了这一点......
我想上面的测试应该可以明确回答我的问题,但它似乎太错了!
也许我在代码中犯了一个错误,或者我只是遗漏了一些东西。不胜感激。
我对套接字的工作方式完全错误吗?
编辑
所以我只是重新运行了我的测试,和上面一模一样,结果几乎一样,只有接收数据的套接字是“notConnected”套接字。
绑定notConnected socket之后连接socket也没有效果。
【问题讨论】:
-
如果先绑定已连接的套接字,然后绑定未连接的套接字会怎样?
-
@AndrewWilliamson 令人惊讶的是,完全没有……同样的结果
-
我刚刚注意到 - 您的问题指出唯一接收数据的套接字是
notConnected套接字,但日志输出显示Connected => message 0 to connected socket。你误读了输出吗? -
@AndrewWilliamson 是的,我添加了一个编辑。似乎它几乎是随机的。有时是一个,有时是另一个......我必须在写问题时重新运行测试并且没有注意到不同的输出。
-
@Luaan:“除此之外什么都做不了”——差不多。它也过滤传入的数据报。您是对的,它不会将无连接套接字转换为面向连接的套接字,但它确实比仅允许调用
Send()而不是SendTo()更有效。