【问题标题】:Sending multiple Async request through socket in c#在c#中通过套接字发送多个异步请求
【发布时间】:2020-04-03 10:43:56
【问题描述】:

我有 ipaddress 列表。我正在使用 udp 协议将请求异步发送到这些地址到 snmp 代理。这些地址回复上的可用 snmp 代理。当我使用这些地址调用 BeginSendTo 时,设备会以随机顺序回复。当调用 ReceiveBeginReceiveFrom 并构造 stateobject 对象时。 T他的 RemoteIPEndPoint 不属于回复的机器,而是属于其他机器。 我调用 BeginSendTo 的方法是否正确? 如果是,y stateobject 实例没有给我正确的远程端点回复? 我正在发布我的代码。如有遗漏请指正。

public class Test
{
    Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    class StateObject
    {
        public Socket Socket { get; set; }
        public byte[] DataBuff { get; set; }
        public IPEndPoint RemoteEndPoint { get; set; }
        //validation are omited
        public static StateObject Create(Socket s,byte[] dataBuf,IPEndPoint endpoint)
        {
            return new StateObject() { Socket = s, DataBuff = dataBuf, RemoteEndPoint = endpoint };
        }

        public static StateObject Transform(object objectstate)
        {
            return objectstate as StateObject;
        }
    }
    public void Fx()
    {
        //list of ip address from 190.188.191.1 - 190.188.191.100
        var address = new[] {
            IPAddress.Parse("190.188.191.1"),
            IPAddress.Parse("190.188.191.100")
        };

        byte[] dataGram = new byte[1024];
        foreach (IPAddress item in address)
        {
            udpSocket.BeginSendTo(dataGram,
                        0,
                        dataGram.Length,
                        SocketFlags.None,
                        new IPEndPoint(item, 161),
                        SendComplete,
                        StateObject.Create(udpSocket, null, new IPEndPoint(item, 161))
                        );
        }
    }

    private void SendComplete(IAsyncResult ar)
    {
        StateObject obj = StateObject.Transform(ar.AsyncState);
        obj.Socket.EndSendTo(ar);//ignore the no of bytes send for now
        byte[] receivedBytes = new byte[1024 * 4];//assume 4kb is enough for response
        var ep = obj.RemoteEndPoint as EndPoint;
        obj.Socket.BeginReceiveFrom(receivedBytes,
            0,
            receivedBytes.Length,
            SocketFlags.None,
            ref ep,
            ReceivedData,
            StateObject.Create(obj.Socket, receivedBytes, obj.RemoteEndPoint)
            );
    }

    private void ReceivedData(IAsyncResult ar)
    {
        StateObject obj = StateObject.Transform(ar.AsyncState);
        var ep = obj.RemoteEndPoint as EndPoint;

        //response received from ip 190.188.191.2 but in stateobject.remoteendpoint will give 190.188.191.1

        var bytesReceived  = obj.Socket.EndReceiveFrom(ar,ref ep);
        byte[] data = new byte[bytesReceived];
        Array.Copy(obj.DataBuff,data, bytesReceived);
    }
}

【问题讨论】:

  • 您标记了UdpClient,但您没有使用它。相反,您使用的是底层 Socket ...您应该在单独的线程上侦听并从收到的数据包中找出发送者(还有消息的顺序和完整性)。这就是 UDP 的工作原理。它是无连接的、不可靠的并且不能维持秩序。你甚至不能假设在一个收到的数据包中消息是完整的......或者你根本不会收到答案。
  • 我也强烈建议你看看 UdpClient 的 Task async API:ReceiveAsyncSendAsync
  • 对于一个ip地址范围如何发送和接收。如何映射发送和接收?
  • 嗯,首先,您对端口有限制。这将是矫枉过正。此外,您需要处理 lost 数据包。也就是说:在生产中,您迟早会发现某些请求根本没有得到响应。这可能有几个原因:远程没有收到请求,远程没有发送响应或远程发送响应但它在途中丢失了。您需要对此进行跟踪,以便您可以重新发出请求(如有必要)或停止等待该响应。否则你会发现你的软件似乎有“内存泄漏”。
  • 感谢您的意见。让我探索其他可能性并尝试解决这个问题。

标签: c# sockets udp endpoint


【解决方案1】:

最后我想出了解决这个扇出多个 udp 请求的问题。状态类型中引入了名为“ID”的新字段。生成 ip 地址时会生成一个唯一标识符。该标识符被分配给状态类型实例并被缓存。此外,此 ID 在请求中发送,以便识别来自特定 IP 地址的响应并从其缓存中检索其状态值。

    Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 
      ProtocolType.Udp);

    Dictionary<int, StateObject> _Cache = new Dictionary<int, StateObject>();

    class StateObject
    {
        public int ID { get; set; }//uniquely identify the response

        public Socket Socket { get; set; }
        public byte[] DataBuff { get; set; }
        public IPEndPoint RemoteEndPoint { get; set; }
        //validation are omited
        public static StateObject Create(Socket s, byte[] dataBuf, IPEndPoint endpoint)
        {
            return new StateObject() { Socket = s, DataBuff = dataBuf, RemoteEndPoint = endpoint };
        }

        public static StateObject Transform(object objectstate)
        {
            return objectstate as StateObject;
        }
    }

    //This function is used to generate range of address and corresponding identifiers
    IEnumerable<KeyValuePair<IPAddress, int>> GenerateMaps(IPAddress start, IPAddress end)
    {
        int count = -1;
        yield return new KeyValuePair<IPAddress, int>(start, ++count);

        //other address in the range

        yield return new KeyValuePair<IPAddress, int>(end, ++count);
    }

我也没有从特定主机 BeginReceiveFrom 接收,而是替换为能够接收和主机的 BeginReceive 函数。标识符的映射有助于解决缓存中的问题。

【讨论】:

    猜你喜欢
    • 2011-06-10
    • 2015-06-09
    • 2012-05-27
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多