c# - 使用 NetworkStream 接收文件随机失败,但始终与 Thread.Sleep() 一起使用

标签 c# sockets tcp network-programming tcpclient

我是网络编程新手,我正在尝试使用 NetworkStream (C#) 将文件从客户端发送到服务器。

我认为下面的代码应该可以工作,但有时(随机)接收端(服务器)将永远循环并且不会收到文件(发送似乎工作正常)。

我让一位助手查看了学校的代码,他提出了以下修复/破解:在进入发送文件的循环之前使用 Thread.Sleep(500)。他没有向我解释为什么这会起作用,他只是说这是发生的事情,原始代码(没有 Thread.Sleep(500))应该可以正常工作(有时确实如此)。

虽然这个黑客似乎解决了这个问题,但我对此并不满意,因为我不太明白它为什么起作用,现在我正在寻找一个更干净/更强大的解决方案来解决这个问题。

这是我的代码:

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace _003
{
    class server
    {
        [STAThread]
        static void Main(string[] args)
        {
            //args = ip, port
            try
            {
                TcpListener myTcpListener = new TcpListener(IPAddress.Parse(args[0]), int.Parse(args[1]));
                myTcpListener.Start();
                Socket sejaSocket;

                while (true)
                {
                    //čakamo na povezavo iz clienta
                    sejaSocket = myTcpListener.AcceptSocket();
                    if (sejaSocket.Connected)
                    {
                        NetworkStream stream = new NetworkStream(sejaSocket);
                        StreamWriter sWriter = new StreamWriter(stream);
                        sWriter.AutoFlush = true;
                        StreamReader sReader = new StreamReader(stream);

                        FileStream myFileStream = new FileStream(@"output.file", FileMode.Create, FileAccess.Write);
                        long rdby = 0;
                        int len;
                        byte[] buffed = new byte[1024];

                        Console.WriteLine("Prejemam datoteko!");

                        int dolzinaDat = int.Parse(sReader.ReadLine());

                        while (rdby < dolzinaDat)
                        {
                            Console.Write("#");

                            len = stream.Read(buffed, 0, buffed.Length);
                            myFileStream.Write(buffed, 0, len);
                            myFileStream.Flush();
                            rdby += len;

                            Console.WriteLine(dolzinaDat + " " + rdby + " " + len.ToString());
                        };

                        stream.Close();
                        myFileStream.Close();

                        Console.WriteLine();
                        Console.WriteLine("Prenos končan!");
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

客户:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace _003
{
    class client
    {
        [STAThread]
        static void Main(string[] args)
        {
            //args = ip, port
            try
            {
                TcpClient sejaTcpClient = new TcpClient();
                sejaTcpClient.Connect(IPAddress.Parse(args[0]), int.Parse(args[1]));

                NetworkStream stream = sejaTcpClient.GetStream();
                StreamWriter sWriter = new StreamWriter(stream);
                sWriter.AutoFlush = true;
                StreamReader sReader = new StreamReader(stream);

                FileStream myFileStream = new FileStream(@"input.file", FileMode.Open, FileAccess.Read);
                long rdby = 0;
                int len;
                byte[] buffed = new byte[1024];

                Console.WriteLine("Pošiljam datoteko!");

                long dolzinaDat = myFileStream.Length;
                sWriter.WriteLine(dolzinaDat);

                //UNCOMMENT THIS AND THE CODE WORKS FINE
                //Thread.Sleep(500);

                while (rdby < dolzinaDat)
                {
                    Console.Write("#");

                    len = myFileStream.Read(buffed, 0, buffed.Length);
                    stream.Write(buffed, 0, len);
                    stream.Flush();
                    rdby += len;

                    Console.WriteLine(dolzinaDat + " " + rdby + " " + len.ToString());
                }

                stream.Close();
                myFileStream.Close();

                Console.WriteLine();
                Console.WriteLine("Prenos končan!");

                Console.WriteLine("Pritisni tipko, da končas");
                Console.ReadKey(true);
                Console.WriteLine("Konec");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

        }

    }
}

任何帮助将不胜感激!

最佳答案

您直接使用流进行混合,并通过 StreamReader 读取它。 StreamReader 可以缓冲读取。也就是说:它可以从文件中读取比您预期更多的内容。这意味着底层 Stream 的读取位置可以比您想象的更进一步。

(Sleep 可能导致 StreamReader 在读取期间超时,并发现它拥有所需的一切。因此它返回,而不会将流推进到第一个之外线)

要么直接从底层流读取而不使用读取器,要么始终使用读取器读取。在这种情况下,您最好使用 BinaryReader,因为您无论如何都想读取二进制文件。

关于c# - 使用 NetworkStream 接收文件随机失败,但始终与 Thread.Sleep() 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20377730/

相关文章:

c# - ViewModel 是否应该公开诸如 BitmapImage 或 ImageSource 或音频文件缓冲区之类的东西?

c# - ASP.NET EF6 - 2 个表和 1 个链接表

c# - 如何清除 Linq To Sql 中的更改?

c++ - 如何从另一个线程唤醒没有超时时间的 Select 调用

支持 SSL/TLS 的 Objective-C TCP 服务器

c# - C# 中字符串的结构映射问题

java - 通过同一个套接字发送多条消息不起作用

c - 使用 SOCK_DGRAM 时使用 recvfrom 接收空负载?

linux - 修改socket结构将数据包发送到另一个IP

objective-c - 使用 "struct sflt_filter"为 TCP 和 UDP 协议(protocol)设置数据过滤器