Странное поведение, при котором UDPClient (слушатель) не будет работать, если будет создан экземпляр перед UDPClient (отправитель) - PullRequest
0 голосов
/ 22 января 2019

Я создал приложение, которое отправляет UDP-сообщение другому приложению, а затем прослушивает ответ UDP-сообщения.Однако происходит самое странное, что я когда-либо видел.В конце концов я обнаружил, что проблема заключается в порядке моего кода, но я не уверен, почему это так.После многих часов я сузил свой код до следующих строк:

Не рабочий код:

    UDPSender sender = new UDPSender(destinationIP, portDest, sourceIP, portSource, AgentTypeEnum.User, this);
    listener = new UDPListener(sourceIP, portSource, AgentTypeEnum.User, this);
    listener.StartListener();
    sender.SendUDPOnce(new BytesAndRequestType(Encoding.ASCII.GetBytes(asciiChars), null), successFlag);

Рабочий код:

    listener = new UDPListener(sourceIP, portSource, AgentTypeEnum.User, this);
    listener.StartListener();
    UDPSender sender = new UDPSender(destinationIP, portDest, sourceIP, portSource, AgentTypeEnum.User, this);
    sender.SendUDPOnce(new BytesAndRequestType(Encoding.ASCII.GetBytes(asciiChars), null), successFlag);

Вот мой код дляспецифика UDPSender и UDPListener.По сути, все, что он делает, - это оборачивает UDPClient и выполняет специфическое прослушивание и отправку пакетов UDP к и от адресатов.

UDPSender:

namespace Agent
{
    public class UDPSender
    {
        private IPAddress destIpAddress;
        private IPAddress sourceIPAddress;
        private int destPortNumber;
        private int sourcePortNumber;
        private byte[] header;         // first part of message byte array
        private SequenceMapSelector mapper = SequenceMapSelector.Instance;
        private Timer udpSendTimer;
        IPEndPoint destinationEP;
        IPEndPoint sourceEP;

        UdpClient sender;

        public UDPSender(string destIpAddress, int destPortNumber, string sourceIPAddress, int sourcePortNumber, AgentTypeEnum mt, ParentAgent pa)
        {
            this.destIpAddress = IPAddress.Parse(destIpAddress);
            this.sourceIPAddress = IPAddress.Parse(sourceIPAddress);
            header = Encoding.ASCII.GetBytes("Apple!");
            // This could have been done with bit shifts and masking
            // header[0] = (byte)(0x424f4f425321 >> 40 & 0xFF);
            // header[1] = (byte)(0x424f4f425321 >> 32 & 0xFF);
            // header[2] = (byte)(0x424f4f425321 >> 24 & 0xFF);
            // header[3] = (byte)(0x424f4f425321 >> 16 & 0xFF);
            // header[4] = (byte)(0x424f4f425321 >> 8 & 0xFF);
            // header[5] = (byte)(0x424f4f425321 & 0xFF);
            // Or this as well
            // header[0] = (byte)((0x424f4f425321 & 0xFF0000000000) >> 40);
            // header[1] = (byte)((0x424f4f425321 << 8 & 0xFF0000000000) >> 40);
            // header[2] = (byte)((0x424f4f425321 << 16 & 0xFF0000000000) >> 40);
            // header[3] = (byte)((0x424f4f425321 << 24 & 0xFF0000000000) >> 40);
            // header[4] = (byte)((0x424f4f425321 << 32 & 0xFF0000000000) >> 40);
            // header[5] = (byte)((0x424f4f425321 << 40 & 0xFF0000000000) >> 40);
            this.destPortNumber = destPortNumber;
            this.sourcePortNumber= sourcePortNumber;
            sourceEP = new IPEndPoint(this.sourceIPAddress, sourcePortNumber);

            sender = new UdpClient
            {
                ExclusiveAddressUse = false
            };
            sender.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            sender.Client.Bind(sourceEP);
            mapper.CreateSystemGroup(this.destIpAddress, mt, pa); // must create a system group so that when data comes in we can look up the group
        }

        public byte SendUDPOnce(BytesAndRequestType udpDataAndCommandUsed, bool successFlag) // i think i can just return void
        {
            byte[] sendbuf = new byte[3 + header.Length + udpDataAndCommandUsed.ASCII_Bytes.Length]; // [0] = lastOctet [1] = sequenceNum;

            byte sequenceNum = BuildByteMessage(header, udpDataAndCommandUsed.ASCII_Bytes, sendbuf, (RequestTypeEnum?)udpDataAndCommandUsed.requestType, successFlag); // I should refactor this to just take a CommandParameters.BytesAndRequestEnum
            destinationEP = new IPEndPoint(destIpAddress, destPortNumber);
            sender.Send(sendbuf, sendbuf.Length, destinationEP);
            // Console.WriteLine("Sender: Message sent to the broadcast address: " + toIpAddress + Environment.NewLine + "Sender: contents (first byte is IP_Octet, second is sequenceNumber): " + string.Join(",", sendbuf));
            return sequenceNum;
        }

        public void SendUDPContinuously(BytesAndRequestType udpDataAndCommandUsed, bool successFlag, int delayMiliseconds)
        {
            udpSendTimer = new Timer(sate => SendUDPOnce(udpDataAndCommandUsed, successFlag), null, 0, delayMiliseconds);
        }

        public void StopUDPContinuousSend()
        {
            mapper.DumpDataToFile(destIpAddress);
            udpSendTimer.Dispose();
        }

        private byte BuildByteMessage(byte[] header, byte[] parameterCommand, byte[] destination, RequestTypeEnum? commandEnum, bool successFlag)
        {
            byte sequenceNum = mapper.CreateNextSequenceByte(destIpAddress, commandEnum);
            Buffer.BlockCopy(header, 0, destination, 0, header.Length);
            destination[header.Length] = (byte)(commandEnum == null ? RequestTypeEnum.ERROR : commandEnum);
            destination[header.Length+1] = sequenceNum;
            destination[header.Length + 2] = (byte)(successFlag ? 1: 0);
            // add header to parameterCommand
            Buffer.BlockCopy(parameterCommand, 0, destination, header.Length + 3, parameterCommand.Length);
            return sequenceNum;
        }
    }
}

UDPListener:

namespace Agent
{
    // in this context, the source is the machine that this code is running on. and the destination is the IP address of the agent machine.
    public class UDPListener
    {
        IPAddress ipAddressSource;
        int listenPort;
        private SequenceMapSelector sequenceMap = SequenceMapSelector.Instance;
        UdpClient listener;
        IPEndPoint endPoint;
        IPEndPoint sendersEndPoint;
        AgentTypeEnum agentTypeOfCurrentMachine;
        ParentAgent pa;

        public UDPListener(string ipAddressOfSource, int portSource, AgentTypeEnum agentTypeOfCurrentMachine, ParentAgent pa)
        {
            this.agentTypeOfCurrentMachine = agentTypeOfCurrentMachine;
            ipAddressSource = IPAddress.Parse(ipAddressOfSource); // need to listen to the IP address this UDP message is coming FROM
            listenPort = portSource;   // need to listen to the the port that the UDP message was sent TO
            this.pa = pa;
        }

        public void StartListener()
        {
            listener = new UdpClient
            {
                ExclusiveAddressUse = false
            };

            listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            endPoint = new IPEndPoint(ipAddressSource, listenPort); //new IPEndPoint(IPAddress.Any, listenPort); this might allow me not to know the IP of this local device!
            listener.Client.Bind(endPoint);

            try
            {
                listener.BeginReceive(new AsyncCallback(Receive), null);
            }
            catch (Exception e)
            {
                ErrorLogger.WriteExceptionToLog(e);
                listener.Close();
            }
            finally
            {

            }
        }

        private void Receive(IAsyncResult res)
        {
            byte[] bytes = listener.EndReceive(res, ref sendersEndPoint);
            listener.BeginReceive(new AsyncCallback(Receive), null);
            if (sequenceMap.FindSystemGroup(sendersEndPoint.Address) == null)
            {
                // if there was no system group for this type of agent, then add a system group for it.
                // are both the AgentType and requestType in bytes[6]??? this seems incorrect
                sequenceMap.CreateSystemGroup(sendersEndPoint.Address, agentTypeOfCurrentMachine, pa); // in this case we don't want (AgentTypeEnum)bytes[6] since this would give us the agent type of the sender machine
                sequenceMap.CreateNextSequenceByte(sendersEndPoint.Address, (RequestTypeEnum)bytes[6]); // in this case we don't want (RequestTypeEnum)bytes[6] since this would give us the agent type of the sender machine
            }
            sequenceMap.AddDataToLookup(sendersEndPoint.Address, sendersEndPoint.Port, bytes[7], bytes);
            Console.WriteLine("Received" + Environment.NewLine);
        }
    }
}

Примечание: оба UDPClients (отправитель и слушатель) привязаны к одному и тому же IP, но с разными номерами портов.Никаких исключений не выбрасывается.Моя теория состоит в том, что сборка мусора может вступить в игру?

Ответ, который я ищу, состоит в том, чтобы объяснить, почему UDPListener никогда не получает никаких сообщений UDP, когда сначала создается мой UDPSender, но если я делаю это наоборотзаказать это работает нормально.

...