c# UDP 服务器数据包处理与同时数据包

本文关键字:数据包 处理 服务器 UDP | 更新日期: 2023-09-27 17:55:58

简短版本

BeginReceiveFrom() 是否处理每个客户端?我的理解是它的回调是根据 ref 的端点触发的。您可以同时进行 2 个接收回调吗?(下面详细概述了我的问题)

长版本

我正在尝试创建一个 UDP 服务器,它将概述以下内容:

  • 管理员可以轻松查看哪些计算机处于联机状态。
  • 监视远程电脑的运行状况
  • 下载有关 PC 的详细信息。
  • 将文件传输到客户端PC并启动批处理/静默安装程序(更新/软件)

我在一家 IT 人员有限的当地政府机构工作。我们在全县拥有超过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+ 图像时需要很长时间。它开始延迟数据包的进程。

我的问题是,在仍然允许其他用户发送/接收请求的同时处理大数据包过程的最佳方法是什么?

c# UDP 服务器数据包处理与同时数据包

BeginReceiveFrom() 是否处理每个客户端?

我认为您对 UDP 有一个基本的错误假设。

UDP 是无连接的,没有"客户端",只有该数据包的发送者。因此:

将文件传输到客户端PC并启动批处理/静默安装程序(更新/软件)

将引导您在UDP之上重新创建HTTP(或FTP)和TCP(可能很糟糕)。UDP 适用于广播和创建 TCP 连接的额外开销很大的情况。

在您的情况下:

  • 管理员可以轻松查看哪些计算机处于联机状态。
  • 监视远程电脑的运行状况
  • 下载有关 PC 的详细信息。
  • 将文件传输到客户端PC并启动批处理/静默安装程序(更新/软件)

前三个比WMI做得更好(使用类Win32_PingStatus 1作为第一个,对于第2和第3个从Win32_ComputerSystemWin32_OperatingSystem开始:WMI的大部分都是关于这两个活动的)。

最后一点:您是否考虑过组策略?


1 这具有易于编程使用的优点 — ping.exe也可以工作,但您需要启动一个单独的进程,捕获其输出,然后解析它。