Серверная обработка пакетов c # UDP с одновременными пакетами - PullRequest
0 голосов
/ 08 мая 2011

Короткая версия

Обрабатывает ли BeginReceiveFrom () для каждого клиента? Насколько я понимаю, его обратный вызов был вызван на основе конечной точки, которая была ref. Можете ли вы иметь 2 OnReceive Callbacks, идущих одновременно? (ниже подробно описываются мои проблемы)

Длинная версия

Я пытаюсь создать UDP-сервер, который будет описывать следующее:

  • Администраторы могут легко увидеть, какие компьютеры подключены к сети.
  • Монитор состояния удаленных ПК
  • Скачать подробную информацию о ПК.
  • Передача файлов на клиентские ПК и запуск пакетных / автоматических установщиков (Обновления / Программное обеспечение)

Я работаю в местном правительственном агентстве с ограниченным ИТ-персоналом. У нас более 300+ компьютеров по всему округу. Эти компьютеры варьируются от мили до 25 миль. Это мой метод, помогающий нам легко следить за состоянием компьютеров и обновлять процессы. Во всяком случае ..

Это запуск моего UDP-сервера ...

private static void ServerStart()
    {
        serv = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 55444);
        EndPoint ep = (EndPoint)ipe;
        serv.Bind(ep);

        try
        {
            LogEvent("Listening...");

            serv.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref ep, new AsyncCallback(initializeConnectionCallback), ep);
        }
        catch (Exception ex)
        {
            LogEvent(ex.Message);
        }
    }

    private static void initializeConnectionCallback(IAsyncResult ar)
    {
        EndPoint epSender = (EndPoint)ar.AsyncState;
        serv.EndReceiveFrom(ar, ref epSender);

        LoginPacket p = new LoginPacket(byteData);
        short opcode = p.getOpcode();
        //LogEvent("OPCODE: " + opcode.ToString());

        if (!clientList.Exists(element => element.strName == p.clientID))
        {
            ClientInfo clientInfo = new ClientInfo();
            clientInfo.endPoint = epSender;
            clientInfo.strName = p.clientID;
            clientInfo.isOnline = true;
            clientList.Add(clientInfo);
            Console.WriteLine("Client: " + clientInfo.strName + " has been added.");
        }
        else
        {
            ClientInfo c = clientList.Find(i => i.strName == p.clientID);
            c.endPoint = epSender;
            c.isOnline = true;
            //Console.WriteLine("[Client already active]");
        }

        ListenForData();
    }

    private static void ListenForData()
    {
        EndPoint endpoint = new IPEndPoint(0, 0);

        serv.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref endpoint, new AsyncCallback(OnReceive), endpoint);
    }

И довольно большой обратный вызов OnReceive, который обрабатывает пакет. (Вот где я считаю, что мои проблемы возникают.) Проблема в том, что только один клиент отправляет пакет, а сервер обрабатывает пакет с этого одного клиента, все в порядке. Например, это часть RequestScreenShotPacket. Это работает хорошо, если только один клиент запрашивает снимок экрана одновременно. Что произойдет, если сервер получит другой вызов во время процесса одного, он начнет объединять 2 пакета вместе, что приведет к получению половины изображения. (другая половина будет повреждена и будет отображать странные цвета и т. д.) Ссылка на поврежденное изображение

private static void OnReceive(IAsyncResult ar)
    {
        try
        {
            EndPoint epSender = (EndPoint)ar.AsyncState;

            bool sendBack = true;

            serv.EndReceiveFrom(ar, ref epSender);

            Packet sendPacket = new Packet();
            byte[] message;

            short opCode = Packet.parseOpcode(byteData);

            sendPacket.insertShort(opCode);

            switch (opCode)
            {
                case PacketHeader.OP_DATA:
                    {
                        DataPacket dp = new DataPacket(byteData);
                        if (dp.DataType == DataPacket.TYPE_REQUESTSCREENSHOT)
                        {
                            sendPacket.insertShort(DataPacket.TYPE_REQUESTSCREENSHOT);
                            LogMessage("Requesting Image...");
                            RequestScreenPacket rsp = new RequestScreenPacket();
                            RequestScreenPacket receivedPacket = (RequestScreenPacket)PacketFactory.getPacket(byteData);
                            rsp.clientID = receivedPacket.clientID;
                            rsp.computerID = receivedPacket.computerID;

                            imageTransferList.AddImageTransfer(receivedPacket.clientID, rsp.computerID, 0);

                            ClientInfo ci = clientList.Find(i => i.strName == receivedPacket.computerID);

                            rsp.Construct();
                            sendPacket = (Packet)rsp;
                            message = sendPacket.Construct();

                            serv.BeginSendTo(message, 0, message.Length, SocketFlags.None, ci.endPoint, new AsyncCallback(OnSend), ci.endPoint);
                            sendBack = false;

                        }
                        else if (dp.DataType == DataPacket.TYPE_RESPONDSCREENSHOT)
                        {
                            sendPacket.insertShort(DataPacket.TYPE_RESPONDSCREENSHOT);
                            ACKPacket ack = new ACKPacket();
                            RespondScreenPacket receivedPacket = (RespondScreenPacket)PacketFactory.getPacket(byteData);
                            ack.ACKTO = receivedPacket.packetFrom;
                            ack.ACKTYPE = DataPacket.TYPE_IMAGEDATA;
                            ack.ACKSEQNUM = 0;

                            ImageTransfer it = imageTransferList.Find(i => i.ImageFrom == receivedPacket.respondTo);
                            if (it == null)
                                LogError("Unable to find ImageTransfer in ImageTransferList");
                            else
                                it.LastSeqNumber = receivedPacket.lastseqnum;

                            ClientInfo ci = clientList.Find(i => i.strName == receivedPacket.respondTo);

                            ack.Construct();
                            sendPacket = (Packet)ack;
                            message = sendPacket.Construct();

                            serv.BeginSendTo(message, 0, message.Length, SocketFlags.None, ci.endPoint, new AsyncCallback(OnSend), ci.endPoint);
                            sendBack = false;

                        }
                        else if (dp.DataType == DataPacket.TYPE_IMAGEDATA)
                        {
                            sendPacket.insertShort(DataPacket.TYPE_IMAGEDATA);
                            ImageDataPacket idp = new ImageDataPacket(byteData);
                            int seqnum = idp.seqnum + 1;
                            int offset = idp.offset;

                            ImageTransfer it = imageTransferList.Find(i => i.ImageFrom == idp.packetFrom);

                            for (int i = 0; i < idp.block.Length; i++)
                            {
                                it.ImageData.Add(idp.block[i]);
                            }

                            ACKPacket ack = new ACKPacket();
                            ack.ACKTYPE = DataPacket.TYPE_IMAGEDATA;
                            ack.ACKSEQNUM = seqnum;

                            ClientInfo ci = clientList.Find(i => i.strName == idp.packetFrom);

                            ack.Construct();
                            sendPacket = (Packet)ack;
                            message = sendPacket.Construct();

                            serv.BeginSendTo(message, 0, message.Length, SocketFlags.None, ci.endPoint, new AsyncCallback(OnSend), ci.endPoint);

                            if (seqnum >= it.LastSeqNumber)
                            {
                                LogMessage("Saving File...");
                                File.WriteAllBytes(@"C:\\" + it.ImageFrom + ".jpg", it.ImageData.ToArray());

                                if (imageTransferList.Contains(it))
                                    imageTransferList.Remove(it);
                            }

                            sendBack = false;

                        }
                        else
                        {
                            LogError("UNKNOWN DATAPACKET WITH DATATYPE: " + dp.DataType.ToString());
                        }

                        break;
                    }

                case PacketHeader.OP_ACK:
                    {
                        sendPacket.insertShort(PacketHeader.OP_ACK);
                        ACKPacket ackPacket = new ACKPacket(byteData);

                        if (ackPacket.ACKTYPE == DataPacket.TYPE_IMAGEDATA)
                        {

                        }

                        break;
                    }

            }

            message = sendPacket.Construct();

            if (sendBack)
            {
                foreach (ClientInfo c in clientList)
                {
                    if (c.endPoint != null)
                    {
                        serv.BeginSendTo(message, 0, message.Length, SocketFlags.None, c.endPoint, new AsyncCallback(OnSend), c.endPoint);
                    }
                }
            }

            ListenForData();
        }
        catch (SocketException ex)
        {
            if (ex.ErrorCode != 10054)
            {
                LogError(ex.Message);
            }
        }
        catch (Exception ex)
        {
            LogError(ex.Message);
        }

    }

Я попытался создать очередь пакетов для обработки пакетов по мере их поступления, но безуспешно. Я чувствую, что именно так я и должен обращаться с очередью, но мои неудачные попытки привели к тому, что очередь пыталась обработать 3+ изображения вплотную. Это начинает задерживать процесс пакетов.

Мой вопрос: как лучше всего обрабатывать большие пакеты, но при этом разрешать другим пользователям отправлять / получать запросы?

1 Ответ

2 голосов
/ 08 мая 2011

Обрабатывает ли BeginReceiveFrom () для каждого клиента?

Я думаю, у вас есть фундаментальное ошибочное предположение о UDP.

UDP не подключен, нет «клиента», только отправитель этого пакета. Следовательно:

Передача файлов на клиентские ПК и запуск пакетных / автоматических установщиков (Обновления / Программное обеспечение)

Приведет вас к воссозданию HTTP (или FTP) и TCP поверх UDP (вероятно, плохо). UDP хорош для широковещательной передачи и в случаях, когда дополнительные издержки при создании TCP-соединения значительны.

В вашем случае:

  • Администраторы могут легко видеть, какие компьютеры подключены к сети.
  • Монитор состояния удаленных ПК
  • Скачать подробную информацию о ПК.
  • Передача файлов на клиентские ПК и запуск пакетных / автоматических установщиков (Обновления / Программное обеспечение)

Первые три намного лучше сделаны через WMI (используя класс Win32_PingStatus 1 для первого, для 2 и и 3 -ого начинаются с Win32_ComputerSystem и Win32_OperatingSystem: большая часть WMI посвящена этим двум действиям).

В заключение: рассматривали ли вы групповую политику?


1 Преимущество заключается в простоте использования программным способом - ping.exe также будет работать, но вам потребуется запустить отдельный процесс, захватить его вывод и затем проанализировать его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...