Short Version
Does BeginReceiveFrom() handle per client? My understanding was its callback was triggered based on the endpoint that was ref. Can you have 2 OnReceive Callbacks going simultaneous? (below outlines my issues in detail)
Long Version
I'm trying to create a UDP server that will outline the follow:
- Administrators can easily see which computers are online.
- Monitor Health of remote PCs
- Download detail information about the PC.
- Transmit files to client PCs and launch batch / silent installers (Updates / software)
I work for a local government agency with limited IT staff. We have upwards of 300+ computers across the county. These computers range anyway from a mile away to 25miles. This is my method of helping us watch the health of the computers and Process updates easily. Anyway..
This is the start of my UDP server...
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);
}
And a fairly large OnReceive Callback that handles the packet. (This is where I believe my issues are occurring.) The problem is as long as only 1 client is sending the packet and the server is processing the packet from that one client all is well. For example this is part of the RequestScreenShotPacket. Which works well as long as only 1 client is requesting the screen shot at a time. What happens if the server receives another call during the process of one, it will start combining the 2 packets together, resulting in half the image. (the other half will be corrupted and it will display strange colors, etc) 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);
}
}
I have tried to come up with a packet queue to handle the packets as they come, but have been unsuccessful. I feel like this is how I should be handling it, with a queue, but my failed attempts have resulting in the queue taking to long when trying to process 3+ images back to back. It starts delaying the process of the packets.
My question is what is the best way to handle the process of large packets while still allowing other users to se开发者_JAVA百科nd/receive requests?
Does BeginReceiveFrom() handle per client?
I think you have a fundamental miss-assumption about UDP.
UDP is connectionless, there is no "client", just the sender of that packet. Hence:
Transmit files to client PCs and launch batch / silent installers (Updates / software)
Will lead you to recreate HTTP (or FTP) and TCP on top of UDP (probably badly). UDP is good for broadcast and cases where the additional overhead of creating a TCP connection is significant.
In you case:
- Administrators can easily see which computers are online.
- Monitor Health of remote PCs
- Download detail information about the PC.
- Transmit files to client PCs and launch batch / silent installers (Updates / software)
The first three are much better done over WMI (using class Win32_PingStatus
1 for the first, for 2nd and 3rd start with Win32_ComputerSystem
and Win32_OperatingSystem
: most of WMI is about these two activities).
For the final point: have you considered Group Policy?
1 This has the advantage of being easy to use programmatically—ping.exe
would also work but you would need to launch a separate process, capture its output and then parse it.
精彩评论