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+ 图像时需要很长时间。它开始延迟数据包的进程。
我的问题是,在仍然允许其他用户发送/接收请求的同时处理大数据包过程的最佳方法是什么?
BeginReceiveFrom() 是否处理每个客户端?
我认为您对 UDP 有一个基本的错误假设。
UDP 是无连接的,没有"客户端",只有该数据包的发送者。因此:
将文件传输到客户端PC并启动批处理/静默安装程序(更新/软件)
将引导您在UDP之上重新创建HTTP(或FTP)和TCP(可能很糟糕)。UDP 适用于广播和创建 TCP 连接的额外开销很大的情况。
在您的情况下:
- 管理员可以轻松查看哪些计算机处于联机状态。
- 监视远程电脑的运行状况
- 下载有关 PC 的详细信息。
- 将文件传输到客户端PC并启动批处理/静默安装程序(更新/软件)
前三个比WMI做得更好(使用类Win32_PingStatus
1作为第一个,对于第2和第3个从Win32_ComputerSystem
和Win32_OperatingSystem
开始:WMI的大部分都是关于这两个活动的)。
最后一点:您是否考虑过组策略?
1 这具有易于编程使用的优点 — ping.exe
也可以工作,但您需要启动一个单独的进程,捕获其输出,然后解析它。