【问题标题】:Sending messages and files over NetworkStream通过 NetworkStream 发送消息和文件
【发布时间】:2011-08-17 21:40:39
【问题描述】:

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

    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是一个TcpClient,一个到服务器的连接)

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

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

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

谢谢!

PS:有什么方法可以确保客户收到所有包裹,如果丢失了,只发送这些并放在一起?

【问题讨论】:

    标签: c# sockets tcpclient


    【解决方案1】:

    如果您使用 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
    }
    

    此外,在我的 sn-p 中,如果您在流上连续发送数据,您可能会读取最后一个接收操作的数量,例如:transfered + bytesRead > msgLengh。您也必须注意这一点。

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

    我只是给你一个ideea,你可以随意微调。

    【讨论】:

    • 那么我现在使用的方法(见代码)究竟应该怎么做呢?就像我提到的,我发送了一个 16 字节的数组,其中包含接下来要发送的文件的包大小。所以我在做什么是完全正确的?
    • 谢谢!这有很大帮助!但是现在我遇到了另一个问题:我尝试在 Ubuntu 的 MonoDevelop 上运行它(Linux-Servers 便宜得多:P)并且效果很好。但是从客户端收到的文件丢失了...... 7kB! (所以无法执行 .exe 文件...)。虽然一开始计算的包大小和我在Windows PC上尝试的完全一样=/
    • 我不知道如何编辑 cmets,所以我在这里写:我也让它在 Linux 的 MonoDevelop 下工作!虽然有点奇怪,但我不得不用你的 while 循环方法替换我的 for 循环才能让它工作 oO
    • 太棒了!自己解决的问题对您最有益,知识明智:)。我自己从未使用过 Mono。
    • 哎呀,现在我通过互联网尝试了(直到现在通过本地网络),他得到了连接,但是数据的发送/检索根本不起作用=/
    猜你喜欢
    • 2020-07-10
    • 1970-01-01
    • 2023-03-07
    • 2014-06-09
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多