c#UDP服务器使用同步数据包处理数据包

时间:2011-05-08 09:16:26

标签: c# .net sockets udp

短版

BeginReceiveFrom()是否按客户端处理?我的理解是它的回调是基于ref的端点触发的。你可以同时进行2次OnReceive回调吗? (下面详细列出了我的问题)

长版

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

  • 管理员可以轻松查看哪些计算机在线。
  • 监控远程PC的运行状况
  • 下载有关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 Callback来处理数据包。 (这是我认为我的问题正在发生的地方。)问题是只要有一个客户端发送数据包,而服务器正在处理来自该客户端的数据包一切都很好。例如,这是RequestScreenShotPacket的一部分。只要一个客户端一次请求屏幕截图,哪个效果很好。如果服务器在一个过程中接收另一个呼叫会发生什么,它将开始将两个数据包组合在一起,从而产生一半的图像。 (另一半会被破坏,它会显示奇怪的颜色等)Link to Corrupt Image

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 个答案:

答案 0 :(得分:2)

  

BeginReceiveFrom()是否按客户端处理?

我认为你对UDP有一个基本的错失假设。

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

  

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

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

在你的情况下:

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

前三个比WMI好得多(首先使用类Win32_PingStatus 1 ,对于2 nd 和3 rd Win32_ComputerSystemWin32_OperatingSystem开头:大多数WMI都与这两项活动有关。)

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


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