C#Socket.SendTo()路由到错误的适配器
我继承了一个UDP发送/接收应用程序。它允许选择一个网络,然后在该网络上发送/接收数据,目的是向所选网络上的所有设备广播数据或从所选网络上的所有设备接收数据。 Receive工作正常,但是当应用程序将数据发送到错误的网络时 令人沮丧的是,它在最初的PC(Windows 7)上运行得很好,但在我以同样方式尝试过的另外两台PC(都是Windows 10)上失败了。 更新了最少的工作示例: 表格1.csC#Socket.SendTo()路由到错误的适配器,c#,sockets,routes,C#,Sockets,Routes,我继承了一个UDP发送/接收应用程序。它允许选择一个网络,然后在该网络上发送/接收数据,目的是向所选网络上的所有设备广播数据或从所选网络上的所有设备接收数据。 Receive工作正常,但是当应用程序将数据发送到错误的网络时 令人沮丧的是,它在最初的PC(Windows 7)上运行得很好,但在我以同样方式尝试过的另外两台PC(都是Windows 10)上失败了。 更新了最少的工作示例: 表格1.cs using System; using System.Timers; using System.W
using System;
using System.Timers;
using System.Windows.Forms;
namespace MinimumWorking
{
public partial class Form1 : Form
{
Network m_Network;
bool m_NetworkStarted;
System.Timers.Timer m_Timer;
public Form1()
{
InitializeComponent();
m_Timer = new System.Timers.Timer(250);
m_Timer.Elapsed += OnTimer;
m_Timer.AutoReset = true;
m_Timer.Enabled = true;
m_Timer.Stop();
m_Network = new Network(this);
}
private void button_StartNetwork_Click(object sender, EventArgs e)
{
if (m_NetworkStarted == false)
{
if (m_Network.InitNetwork("225.0.0.37", 2031))
{
button_StartNetwork.Text = "Stop";
m_NetworkStarted = true;
m_Timer.Start();
}
}
else
{
m_Network.Close();
button_StartNetwork.Text = "Start";
m_NetworkStarted = false;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_Timer.Stop();
if (m_NetworkStarted == true)
{
m_Network.Close();
}
}
private void OnTimer(object sender, ElapsedEventArgs e)
{
byte[] msg = new byte[] { 0x54, 0x45, 0x53, 0x54 };
m_Network.SendPacket(new Packet(msg, msg.Length));
}
}
}
Form1.designer.cs(我应该发布所有这些吗?)
我已检查是否选择了正确的IP地址。我还尝试设置静态路由并拔下主适配器(数据无法发送)。
知道怎么回事吗?我可以通过将传输套接字显式绑定到选定的IP地址来解决问题。我的传输套接字初始化变为:
m_transmitSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ipAddress = IPAddress.Parse(ipAddr);
IPAddress localIPAddr = IPAddress.Parse(m_form.comboBox_Config_Interfaces.Text);
IPEndPoint localIPEndPoint = new IPEndPoint(localIPAddr, port);
m_transmitSocket.Bind(localIPEndPoint);
m_transmitSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, localIPAddr));
m_transmitSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
m_ipEndPointTx = new IPEndPoint(ipAddress, port);
我仍然不明白,为什么在没有绑定的情况下,在原来的PC上这样做是可行的,或者为什么更改子网掩码会影响使用的网络(请参阅@jdweng对该问题的评论)接口的掩码是错误的。从cmd.exe>IPCONFIG/ALL检查子网掩码。路由使用具有最受限制的掩码的路由。因此,路由将使用255.255.255.0.0之前的掩码255.255.255.0。如果没有良好的掩码,则无法回答您的问题。也就是说,多播的关键在于数据报由同一信道上的任何侦听器接收。通道没有分配给每个适配器,因此不清楚为什么您希望数据报在特定适配器上接收。@jdweng有趣的是,接口确实有一个限制较少的掩码。当我更改此掩码(我现在有3个接口,所有接口都带有掩码255.255.255.0)时,数据开始在不同但仍然不正确的接口上发送。还有什么决定接口“优先级”的因素吗?@PeterDuniho用最少的工作示例进行了更新(抱歉,深夜的问题)。PC机连接到两个网络,一个用于一般内部网通信,另一个用于各种嵌入式设备。其目的是将数据广播到一个网络或另一个网络上的所有设备。@jdweng我尝试更改子网掩码,以使我的加密网络受到最严格的限制,但数据仍在另两个网络之一上发送。您在原始代码中从未使用过变量L:ocalIPEndPoint。它修复了TTL吗?Ping是与UDP和TCP不同的协议(ARP)。因此,您可以ping以确保存在从一个IP到另一个IP的路由。是的,我必须创建localIPEndPoint并绑定到它来解决问题,它不在原始代码中。传出数据包上的TTL仍然为1。如果我ping网络上的一个设备,TTL是64,但我不是向特定设备发送数据包,而是尝试广播它们。
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Windows.Forms;
namespace MinimumWorking
{
public class Packet
{
public int m_length;
public byte[] m_data;
public Packet (byte[] data, int length)
{
m_data = new byte[length];
Array.Copy(data, m_data, length);
m_length = length;
}
}
class Network
{
Form1 m_form;
AutoResetEvent m_resetEvent;
Thread m_readerThread;
public bool m_receiving { get; set; }
Thread m_senderThread;
public bool m_sending { get; set; }
Queue<Packet> m_MessageQueue = new Queue<Packet>();
IPEndPoint m_ipEndPointRx;
IPEndPoint m_ipEndPointTx;
Socket m_readSocket;
Socket m_transmitSocket;
Object m_lock;
public Network (Form1 form)
{
m_form = form;
m_lock = new object();
PopulateInterfaces();
}
public bool InitNetwork(String ipAddr, int port)
{
m_resetEvent = new AutoResetEvent(false);
try
{
m_transmitSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ipAddress = IPAddress.Parse(ipAddr);
IPAddress ipIntAddress = IPAddress.Parse(m_form.comboBox_Config_Interfaces.Text);
m_transmitSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, ipIntAddress));
m_transmitSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
m_ipEndPointTx = new IPEndPoint(ipAddress, port);
}
catch (SocketException)
{
return false;
}
m_sending = true;
m_senderThread = new Thread(new ThreadStart(SendThread));
m_senderThread.Start();
try
{
m_readSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ipAddressRx = IPAddress.Parse(ipAddr);
m_ipEndPointRx = new IPEndPoint(IPAddress.Any, 2040);
m_readSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
m_readSocket.Bind(m_ipEndPointRx);
IPAddress ipIntAddressRx = IPAddress.Parse(m_form.comboBox_Config_Interfaces.Text);
m_readSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddressRx, ipIntAddressRx));
m_readSocket.ReceiveTimeout = 100;
}
catch (SocketException)
{
return false;
}
m_receiving = true;
m_readerThread = new Thread(new ThreadStart(ReadData));
m_readerThread.Start();
return true;
}
void PopulateInterfaces()
{
List<String> interfaces = new List<String>();
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (var nic in ips)
{
if (nic.AddressFamily == AddressFamily.InterNetwork)
{
interfaces.Add(nic.ToString());
m_form.comboBox_Config_Interfaces.Items.Add(nic.ToString());
}
}
m_form.comboBox_Config_Interfaces.SelectedIndex = 0;
}
public void Close()
{
m_transmitSocket.Close();
m_sending = false;
m_resetEvent.Set();
m_readSocket.Close();
m_receiving = false;
}
public void SendPacket(Packet packet)
{
lock (m_lock)
{
m_MessageQueue.Enqueue(packet);
}
m_resetEvent.Set();
}
public void SendThread()
{
try
{
while (m_sending)
{
m_resetEvent.WaitOne();
lock (m_lock)
{
foreach (var message in m_MessageQueue)
{
if (m_transmitSocket != null)
{
m_transmitSocket.SendTo(message.m_data, m_ipEndPointTx);
}
}
m_MessageQueue.Clear();
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private void ReadData()
{
byte[] b = new byte[1024];
EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
while (m_receiving)
{
try
{
if (m_readSocket.ReceiveFrom(b, ref endPoint) > 0)
{
// Parse the message
}
}
catch (SocketException)
{
// Timeout
}
}
}
}
}
Windows IP Configuration
Ethernet adapter VirtualBox Host-Only Network:
Connection-specific DNS Suffix . :
IPv4 Address. . . . . . . . . . . : 192.168.56.1
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
Ethernet adapter Ethernet:
Connection-specific DNS Suffix . : lan
IPv4 Address. . . . . . . . . . . : 192.168.1.25
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.1.1
Ethernet adapter Ethernet 2:
Connection-specific DNS Suffix . :
IPv4 Address. . . . . . . . . . . : 172.16.15.34
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
m_transmitSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress ipAddress = IPAddress.Parse(ipAddr);
IPAddress localIPAddr = IPAddress.Parse(m_form.comboBox_Config_Interfaces.Text);
IPEndPoint localIPEndPoint = new IPEndPoint(localIPAddr, port);
m_transmitSocket.Bind(localIPEndPoint);
m_transmitSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, localIPAddr));
m_transmitSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
m_ipEndPointTx = new IPEndPoint(ipAddress, port);