关于NetworkStream的传输的几个问题

iloli 2014-01-30 11:08:24
我有个项目,其中使用到NetworkStream在网络上进行文件传输。由于本人第一次做这种大文件1M以上的网络传输,在实践中发现发送端将一个4M的文件分块(64K)发送时,接收端如果以8K以下的块接收是正常的,而以大于8K的块来接收就会造成接收到的文件的最后会有部分数据缺失的情况。而且接收的缓存块越大于8K 则最后的数据缺失得越严重。(注明,当我发小文件的时候即使是分了更小的块发送也是正常的,不会有问题)

不是太了解NetworkStream的传输机制,但我好像知道有一个NetworkStream的接收或发送缓存, 这个是不是会影响到接收代码的逻辑?比如当接收端接收不过来的时候 会造成NetworkStream缓存被发送端填满,而如果没有握手机制,发送端不管这个 会继续发送,而接收端的缓存满了以后 ,就会把这些新发过来的数据直接丢掉呢?

如果是这样,那是不是即使是使用TCP协议传输文件,分块的时候也必须在接收端的代码中手动发回对每个块的确认信息给发送端,然后发送端再发下一个块的数据呢?但感觉这样操作岂不是和UDP传输一样了?


有经验做过这方面东西的朋友能不能给一个比较健壮的发送接收的具体详细点的过程逻辑给我。。
...全文
666 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
iloli 2014-02-08
  • 打赏
  • 举报
回复
引用 8 楼 feiyun0112 的回复:
你用比较工具看看,接收和发送的文件内容从哪开始不一样的
我找到关键原因了。。 我设置的接收缓存不能超过8K 是因为TcpClient的ReceiveBufferSize属性默认为8192字节也就是8K。所以当我的接收缓存数组设置得过大,就会在实际工作中可能造成TcpClient的缓存中最大的缓存数据都不够我接收的,从而在接收循环中再一次接收下一批次缓存时,由于计算机这个速度太快了,而接收缓存上次已经接收完,新的数据还没有收到,而造成此次接收时已经接收缓存中已经没有可用的接收数据了,所有文件尾部就缺失了数据。 我一但把TcpClient的ReceiveBufferSize的值设得大于或等于我的接收缓存数组就解决问题了。还有一个就是我还发现TcpClient的发送缓存SendBufferSize和接收缓存ReceiveBufferSize的设置值不能相差太多,如发送缓存为4K 而接收缓存为4M 这样也容易出现问题。 所有我还有一个疑问就是:如果发送端和接收端是分二个人写的,那岂不是要问到另一端的人设置的缓存大小才行。。。这个真是很杯具。。
iloli 2014-02-07
  • 打赏
  • 举报
回复
引用 3 楼 caozhy 的回复:
注意 DataAvailable http://social.msdn.microsoft.com/Forums/en-US/759f3f2f-347b-4bd8-aa05-fb7f681c3426/networkstreamread-does-not-always-read-the-full-string-returned-from-the-server
下面是部分代码。。。

 private void ReceiveByte(byte[] receiveByte)
        {
            IAsyncResult iar = ns.BeginRead(receiveByte, 0, receiveByte.Length, null, null);
            int count = sleepCount;
            if (!iar.IsCompleted)
            {
                if (count <= 0)
                {
                    receiveByte = null;
                    return;
                }
                Thread.Sleep(sleepMilliseconds);
                count--;
            }
            try
            {
                ns.EndRead(iar);
                return;
            }
            catch (Exception)
            {
                receiveByte = null;
                return;
            }

public bool Send(byte[] sendByte,int offset,int size )
        {
            IAsyncResult iar = ns.BeginWrite(sendByte, offset,size, null, null);
            int count = sleepCount;
            if (!iar.IsCompleted)
            {
                if (count <= 0)
                {
                    return false;
                }
                Thread.Sleep(sleepMilliseconds);
                count--;
            }
            try
            {
                ns.EndWrite(iar);
                ns.Flush();
                return true;
            }
            catch (Exception)
            {
                return false;
            }


        }


 public bool SendFile(string path, int bufferSize)
        {
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            int count = sleepCount; //循环次数
            int readCount = 0;  //当前读到的文件流字节数
            byte[] sendBufs = new byte[bufferSize]; //发送缓存
            
            int fileLength = (int)fs.Length;
            string fileName = Path.GetFileName(path);
            byte[] packetFirst = Joinhead4(string.Format("|{0}|{1}|", fileName, fileLength.ToString()));

            if (!pt.Send(packetFirst))  //发送头包
                return false;

            #region 关键代码
            while ((readCount = fs.Read(sendBufs,0,sendBufs.Length)) != 0)
            {//以缓存大小分片循环发送文件内容

                if (!pt.Send(sendBufs,0,readCount))
                {
                    return false;
                }
            }

            fs.Close();
            #endregion

            return true;
        }

public void ReceiveFile(string directoryPath,int bufferSize)
        { 
            //先接收头包 以得到文件名和文件长度
            byte [] packetFirst = pt.ReceiveOnePacket(4);//根据4字节包头指定长度接收头包
            string rec = DropHead4(packetFirst);//去掉包头得到头包字符串信息
            rec = rec.Remove(0, 1);//去掉最前的分隔线
            rec = rec.Remove(rec.Length - 1, 1);//去掉最后的分隔线
            string[] fileds = rec.Split(new string[] { "|" }, StringSplitOptions.None);//得到数据字段

            string fileName = fileds[0];
            string fileFullPath = directoryPath + fileds[0];
            int fileLength = int.Parse(fileds[1]);


            FileStream fs = new FileStream(fileFullPath, FileMode.Create, FileAccess.ReadWrite);
            
            
            //循环接收文件内容
            int receiveLength = fileLength;   //需要接收的文件总字节数
            byte[] recBufs = new byte[bufferSize]; //接收缓存

            while (receiveLength > 0)
            {
                recBufs = pt.ReceiveLength(recBufs.Length);

                if (receiveLength > recBufs.Length)
                {
                    fs.Write(recBufs, 0, recBufs.Length);
                }
                else
                {
                    fs.Write(recBufs, 0, receiveLength);
                }
                fs.Flush();
                receiveLength -= recBufs.Length;
            }
            
            //wr.Close();
            fs.Close();
        }

iloli 2014-02-07
  • 打赏
  • 举报
回复
引用 3 楼 caozhy 的回复:
注意 DataAvailable http://social.msdn.microsoft.com/Forums/en-US/759f3f2f-347b-4bd8-aa05-fb7f681c3426/networkstreamread-does-not-always-read-the-full-string-returned-from-the-server
数据可用?我是用iar.IsCompleted 来判断每次异步接收是否完成,完成的话就把当次接收到缓存里的数据写入文件,然后再循环接收,直到接收完全部文件长度。文件长度是一开始就在包头信息中发送过来的。 我现在仔细测试了一下。发送的时候发送缓存字节可以任意设,,从K到M 级别都行。但是接收缓存字节不能超过8K ,8K 以下含8K 接收正常,一但超过8K 就出问题,接收的文件会缺失尾部一些数据,而且接收缓存设得越大,文件尾部数据缺失得越严重。。
iloli 2014-02-07
  • 打赏
  • 举报
回复
引用 8 楼 feiyun0112 的回复:
你用比较工具看看,接收和发送的文件内容从哪开始不一样的
数据只是缺少,不会不一样。而且每次缺少的都是文件尾部的数据,缺少的数量不固定。有时少有时多。根据测试当接收缓存设得越大,尾部缺失得越多
feiyun0112 2014-02-07
  • 打赏
  • 举报
回复
你用比较工具看看,接收和发送的文件内容从哪开始不一样的
iloli 2014-02-07
  • 打赏
  • 举报
回复
引用 6 楼 feiyun0112 的回复:
if (!iar.IsCompleted) 应该是 while (!iar.IsCompleted) 循环等待吧 ***************************************************************************** 签名档: http://feiyun0112.cnblogs.com/
谢谢 这是个问题,可能是我手误。但我使用了WHILE后还是一样的情况。。 我看书上的例子是把整个发送或接收过程封装成一个方法,再去异步执行这个方法,和我直接执行NETWORKSTREAM的异步方法应该是一样的吧。
feiyun0112 2014-02-07
  • 打赏
  • 举报
回复
if (!iar.IsCompleted)
应该是
while (!iar.IsCompleted)
循环等待吧

*****************************************************************************
签名档: http://feiyun0112.cnblogs.com/
iloli 2014-01-30
  • 打赏
  • 举报
回复
引用 1 楼 liuqian4243 的回复:
过年了,帮顶下
谢谢。
Ny-6000 2014-01-30
  • 打赏
  • 举报
回复
过年了,帮顶下
threenewbee 2014-01-30
  • 打赏
  • 举报
回复
注意 DataAvailable http://social.msdn.microsoft.com/Forums/en-US/759f3f2f-347b-4bd8-aa05-fb7f681c3426/networkstreamread-does-not-always-read-the-full-string-returned-from-the-server

17,741

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 .NET Framework
社区管理员
  • .NET Framework社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧