问题
I am quite new to network programming and I have a few questions regarding this code:
if (client.Connected)
{
ChangeLabel("Mit dem Server verbunden...");
NetworkStream stream = client.GetStream();
FileStream fs = null;
try
{
fs = new FileStream("Mapeditor2.exe", FileMode.Create);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
Environment.Exit(0);
}
byte[] bResponse = new byte[16];
stream.Read(bResponse, 0, 16);
string sResponse = System.Text.Encoding.UTF8.GetString(bResponse);
int NoOfPackets = Convert.ToInt32(sResponse);
float progress = 0;
float progressPercent = 100.0f / (float)NoOfPackets;
byte[] buffer = new byte[128];
int bytesRead;
for (int i = 0; i < NoOfPackets; i++)
{
bytesRead = stream.Read(buffer, 0, 128);
fs.Write(buffer, 0, bytesRead);
progress += progressPercent;
ChangeProgress((int)progress);
}
fs.Close();
stream.Close();
}
(Client is a TcpClient, a connection to a server)
Now I try to make an updater for my mapeditor, as you can see. First I send a 16-byte message containing the number of packages that will be send afterwards (the Mapeditor.exe file!), this is for the progressbar of the client...
Is there any dynamic way to do this? (not saying "read a 16 byte array" and write dynamically text and files into the stream with the client automatically knowing when he has to read text and when files)
I hope so, or is there any other way of writing an updater/patcher? How do game developers do this?
Thanks!
PS: Is there any way to make sure the client receives ALL packages, and if some got lost, only send these and put them together?
回答1:
If you are using TCP, the protocol takes care of ordering, retransmitting and so on.
Regarding sending/receiving data dynamically, you can use a prefix protocol in which you first send a number (say an int - 4 bytes) that represents the length of the message to come. After that you send the rest of the message.
The receiver waits for 4 bytes, then converts them into an integer and waits for that amount of bytes. This process repeats again and again.
In your case, there is no point in reading 16 bytes first, parsing that into a string, and then parsing the string into an int. Your sender can convert the int into bytes right away like this:
// lengthBytes.Length = 4 bytes, which is sizeof(int)
byte[] lengthBytes = BitConverter.GetBytes(anInt);
end then sending it on the wire.
Then, at the receiving end, in your code, you do like:
byte[] msgLengthBytes = new byte[sizeof(int)]; // or hardcode 4 here, I'm a generalization junkie
stream.Read(msgLengthBytes, 0, msgLengthBytes.Length);
int msgLength = BitConverter.GetInt32(msgLengthBytes, 0);
Also, instead of that for which assumes that every time you read from the stream you read exactly the amount of bytes you are expecting, you should use something like this:
int transfered = 0;
while (transfered < msgLength)
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, bytesRead);
transfered += bytesRead;
progress += (bytesRead / msgLength) * 100;
ChangeProgress(progress); // You can't use int here anymore, use Math to round or something, for your progress bar
}
Also, in my snippet it is possible that the last receive operation you will read an amount such as: transfered + bytesRead > msgLengh, if you send data continuously on the stream. You must take care of that as well.
Anyhow, if I were you and because you need some sort of a progress notifier, I'd use the stream with the async BeginRead().
I just gave you an ideea, you can fine-tune it as you wish.
来源:https://stackoverflow.com/questions/7099875/sending-messages-and-files-over-networkstream