通过NetworkStream发送消息和文件

时间:2011-08-17 21:40:39

标签: c# sockets tcpclient

我对网络编程很陌生,我对这段代码有几个问题:

    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();
            }

(客户端是TcpClient,与服务器的连接)

现在我尝试为我的mapeditor制作更新程序,如您所见。 首先,我发送一个16字节的消息,其中包含之后将发送的包的数量(Mapeditor.exe文件!),这是用于客户端的进度条...

有没有动态的方法来做到这一点? (不是说“读取16字节数组”并将文本和文件动态写入流中,客户端自动知道何时必须阅读文本和文件)

我希望如此,还是有其他方式来编写更新程序/修补程序?游戏开发者如何做到这一点?

谢谢!

PS:有没有办法确保客户端收到所有包裹,如果有人丢失了,只发送这些包裹并将它们放在一起?

1 个答案:

答案 0 :(得分:2)

如果您使用TCP,协议将负责订购,重新传输等。

关于动态发送/接收数据,您可以使用前缀协议,在该协议中首先发送一个数字(比如一个int - 4字节),表示要发送的消息的长度。之后,您发送剩余的消息。

接收器等待4个字节,然后将它们转换为整数并等待该字节数。这个过程一次又一次地重复。

在您的情况下,首先读取16个字节,将其解析为字符串,然后将字符串解析为int是没有意义的。您的发件人可以立即将int转换为字节,如下所示:

// lengthBytes.Length = 4 bytes, which is sizeof(int)
byte[] lengthBytes = BitConverter.GetBytes(anInt);

然后将其发送到电线上。

然后,在接收端,在您的代码中,您确实喜欢:

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);

此外,假设您每次从流中读取时都会读取您期望的字节数,而不是假设您应该使用以下内容:

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
}

另外,在我的代码片段中,您可能会读取最后一个接收操作,例如:transfered + bytesRead&gt; msgLengh,如果你在流上连续发送数据。你也必须照顾好。

无论如何,如果我是你,并且因为你需要某种进度通知器,我会使用带有异步BeginRead()的流。

我刚给你一个想法,你可以随意微调它。