C# 通过NetworkStream传输项目会导致某些数据被阻塞

C# 通过NetworkStream传输项目会导致某些数据被阻塞,c#,.net,tcpclient,networkstream,C#,.net,Tcpclient,Networkstream,我正在发送文件名(字符串)、文件大小(int)和文件(字节[])。在某些情况下,取决于服务器端处理数据的速度,NetworkStream已经读取了我还不需要的数据 示例:我执行.Read以获取文件名,我将获取文件名、文件大小和文件原始数据的数据。我假设发生这种情况是因为服务器只是在第一个.Read尚未执行时执行.Write并将数据写入流。这会破坏我的文件大小。读取。现在,当我对文件大小执行.Read时,我显示了一个巨大的数字,当我对文件本身进行读取并根据读取文件大小分配一个新字节[]时,我得到一

我正在发送文件名(字符串)、文件大小(int)和文件(字节[])。在某些情况下,取决于服务器端处理数据的速度,NetworkStream已经读取了我还不需要的数据

示例:我执行.Read以获取文件名,我将获取文件名、文件大小和文件原始数据的数据。我假设发生这种情况是因为服务器只是在第一个.Read尚未执行时执行.Write并将数据写入流。这会破坏我的文件大小。读取。现在,当我对文件大小执行.Read时,我显示了一个巨大的数字,当我对文件本身进行读取并根据读取文件大小分配一个新字节[]时,我得到一个OutOfMemory异常

如何正确同步读取?我在网上找到的例子就是这样做的

一些代码:

   private void ReadandSaveFileFromServer(TcpClient clientATF, NetworkStream currentStream, string locationToSave)
    {
        int fileSize = 0;
        string fileName = "";
        int readPos = 0;
        int bytesRead = -1;

        fileName = ReadStringFromServer(clientATF, currentStream);

        fileSize = ReadIntFromServer(clientATF, currentStream);


        byte[] fileSent = new byte[fileSize];

        while (bytesRead != 0)
        {
            if (currentStream.CanRead && clientATF.Connected)
            {

                bytesRead = currentStream.Read(fileSent, readPos, fileSent.Length);

                readPos += bytesRead;
                if (readPos == bytesRead)
                {
                    break;
                 }

            }
            else
            {
                WriteToConsole("Log Transfer Failed");
                break;
            }
        }
        WriteToConsole("Log Recieved");

        File.WriteAllBytes(locationToSave + "\\" + fileName, fileSent);


    }


 private string ReadStringFromServer(TcpClient clientATF, NetworkStream currentStream)
    {
        int i = -1;
        string builtString = "";
        byte[] stringFromClient = new byte[256];



            if (clientATF.Connected && currentStream.CanRead)
            {

                i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
                builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);

            }

            else
            {
                return "Connection Error";
            }



        return builtString;

    }

    private int ReadIntFromServer(TcpClient clientATF, NetworkStream currentStream)
    {
        int i = -1 ;
        int builtInteger = 0;
        byte[] integerFromClient = new byte[256];
        int offset = 0;


            if (clientATF.Connected && currentStream.CanRead)
            {

                i = currentStream.Read(integerFromClient, offset, integerFromClient.Length);

                builtInteger = BitConverter.ToInt32(integerFromClient, 0);

            }

            else
            {
                return -1;
            }



        return builtInteger;
    }
我试过使用偏移量…但没有luch。谢谢你的帮助

我开始问另一个问题,但它涉及到其他问题

提前谢谢 肖恩

编辑:这是我的发送字符串代码:

  private void SendToClient( TcpClient clientATF,  NetworkStream currentStream, string messageToSend)
    {
        byte[] messageAsByteArray = new byte[256];

        messageAsByteArray = Encoding.ASCII.GetBytes(messageToSend);

        if (clientATF.Connected && currentStream.CanWrite)
        {
            //send the string to the client

                currentStream.Write(messageAsByteArray, 0, messageAsByteArray.Length);

        }

    }

TCP/IP是流式传输,而不是数据报,因此您想要的行为并不存在。您可以通过让流包含足够的信息以进行解析来解决这个问题


换句话说,您可以在一行文本后使用分隔符,例如CR/LF,或者您可以给出即将到来的数据段的长度。您还可以在适当的情况下使用固定大小的字段。

TCP/IP是流式传输,而不是数据报,因此您想要的行为并不存在。您可以通过让流包含足够的信息以进行解析来解决这个问题


换句话说,您可以在一行文本后使用分隔符,例如CR/LF,或者您可以给出即将到来的数据段的长度。您还可以在适当的情况下使用固定大小的字段。

您不能依赖于一次从流中读取多少字节,假设字符串的长度固定(256),则您的
ReadStringFromServer
方法中存在错误

而不是:

 i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
 builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);
尝试:

do
{
i=currentStream.Read(stringFromClient,0,256-builtString.Length);
builtString+=(System.Text.Encoding.ASCII.GetString(stringFromClient,0,i));
}while(builtString.Length<256)

您不能依赖于一次从一个流中读取多少字节,假设字符串的长度固定(256),则您的
ReadStringFromServer
方法中存在错误

而不是:

 i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
 builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);
尝试:

do
{
i=currentStream.Read(stringFromClient,0,256-builtString.Length);
builtString+=(System.Text.Encoding.ASCII.GetString(stringFromClient,0,i));
}while(builtString.Length<256)

一个更好的解决方案可能是将所有数据序列化(例如,转换为JSON)并通过网络流传递所有数据。

一个更好的解决方案可能是将所有数据序列化(例如,转换为JSON)并通过网络流传递所有数据。

Read()您调用它的方式将占用256个字节;这就是stringFromClient.Length的设置

有两种方法可以精确地分割数据流,这两种方法都需要知道或创建某种方法来计算数据之间的边界。它们是分隔流和固定长度流

对于分隔格式,可以选择一个不会用作文件名或大小的有效部分的字符(管道、空格、制表符、换行符等),然后在文件名和大小之间以及大小和文件内容之间插入一个字符。然后,一次读取一个字节到数组中,直到找到一个分隔符。到目前为止,除了分隔符之外,您已经读取的字节是您的数据;提取成可用的形式并返回。这通常会使流变短,但要求永远不要使用一个可能的字符

对于固定大小的格式,确定数据的任何合理值都不会超过的字节数。例如,文件名不能超过256个字符(更有可能不超过50个字符;一些较旧/较简单的操作系统仍然限制为8个字符)。在任何Windows环境中,文件大小不能超过2^64字节,该数字可以用4字节的原始数字数据或20个字符的字符串表示。因此,无论您选择何种限制,请使用适当的缓冲区填充数据;对于原始数字,强制转换为Int64并将其切碎为字节,而对于字符串,则填充空格。然后,您知道前X字节将是文件名,下一个Y字节将是文件大小,之后的任何内容都是内容。这会使流变大,但内容可以是任何内容,因为没有特殊或保留的字节值。

Read()您调用它的方式将提取256个字节;这就是stringFromClient.Length的设置

有两种方法可以精确地分割数据流,这两种方法都需要知道或创建某种方法来计算数据之间的边界。它们是分隔流和固定长度流

对于分隔格式,可以选择一个不会用作文件名或大小的有效部分的字符(管道、空格、制表符、换行符等),然后在文件名和大小之间以及大小和文件内容之间插入一个字符。然后,一次读取一个字节到数组中,直到找到一个分隔符。到目前为止,除了分隔符之外,您已经读取的字节是您的数据;提取成可用的形式并返回。这通常会使流变短,但要求永远不要使用一个可能的字符

对于固定大小的格式,确定数据的任何合理值都不会超过的字节数。例如,文件名不能超过256个字符(更有可能不超过50个字符;一些较旧/较简单的操作系统仍然限制为8个字符)。在任何Windows环境中,文件大小都不能超过2^64字节,该数字可以用4字节的原始数字数据或20个字符表示