代码之家  ›  专栏  ›  技术社区  ›  Olha Shumeliuk

使TcpClient等待,直到写入数据

  •  2
  • Olha Shumeliuk  · 技术社区  · 8 年前

    我想通过tcp将数据发送到特定的ip端口 我已经编写了一个示例,它应该发送一些字符串:

    internal class TcpSender : BaseDataSender
    {
        public TcpSender(Settings settings) : base(settings)
        {
        }
    
        public async override Task SendDataAsync(string data)
        {
            Guard.ArgumentNotNullOrEmptyString(data, nameof(data));
    
            byte[] sendData = Encoding.UTF8.GetBytes(data);
            using (var client = new TcpClient(Settings.IpAddress, Settings.Port))
            using (var stream = client.GetStream())
            {
                await stream.WriteAsync(sendData, 0, sendData.Length);
            }
        }
    }
    

    这里的问题是,我的流在tcp客户端发送所有数据之前被释放。我应该如何重写我的代码来等待所有的数据被写入,然后才处理所有的资源?谢谢

    UPD:从控制台util调用:

    static void Main(string[] args)
    {
        // here settings and date are gotten from args
        try
        {
            GenerateAndSendData(settings, date)
                    .GetAwaiter()
                    .GetResult();
        }
        catch (Exception e)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(e);
        }
    }
    
    public static async Task GenerateAndSendData(Settings settings, DateTime date)
    {
        var sender = new TcpSender(settings);
        await sender.SendDataAsync("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.");
    }
    

    class TcpEchoServer
    {
        static TcpListener listen;
        static Thread serverthread;
    
        public static void Start()
        {
            listen = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 514);
            serverthread = new Thread(new ThreadStart(DoListen));
            serverthread.Start();
        }
    
        private static void DoListen()
        {
            // Listen
            listen.Start();
            Console.WriteLine("Server: Started server");
    
            while (true)
            {
                Console.WriteLine("Server: Waiting...");
                TcpClient client = listen.AcceptTcpClient();
                Console.WriteLine("Server: Waited");
    
                // New thread with client
                Thread clientThread = new Thread(new ParameterizedThreadStart(DoClient));
                clientThread.Start(client);
            }
        }
    
        private static void DoClient(object client)
        {
            // Read data
            TcpClient tClient = (TcpClient)client;
    
            Console.WriteLine("Client (Thread: {0}): Connected!", Thread.CurrentThread.ManagedThreadId);
            do
            {
                if (!tClient.Connected)
                {
                    tClient.Close();
                    Thread.CurrentThread.Abort();       // Kill thread.
                }
    
                if (tClient.Available > 0)
                {
                    byte pByte = (byte)tClient.GetStream().ReadByte();
                    Console.WriteLine("Client (Thread: {0}): Data {1}", Thread.CurrentThread.ManagedThreadId, pByte);
                    tClient.GetStream().WriteByte(pByte);
                }
    
                // Pause
                Thread.Sleep(100);
            } while (true);
        }
    }
    
    3 回复  |  直到 8 年前
        1
  •  1
  •   pere57    8 年前

    简单的一点是,echo服务器工作缓慢,因为每次读取后都会暂停100毫秒。我想是这样你才有机会看到发生了什么。

    • 当你的客户执行离开 using 块,流被处置(感谢克雷格。费伊在他的 answer 用于指出执行在底层套接字完成数据的物理传输之前进行)
    • 处理 NetworkStream Socket
    • 停产给了 在缓冲数据最终关闭之前完成发送。参考文献: Graceful Shutdown, Linger Options, and Socket Closure
    • 网络流 它将所有写操作直接传递到套接字。因此,即使在传输完成之前释放了网络流,也不会丢失任何数据
    • 关闭中的套接字可以完成现有请求,但不接受新请求。

    因此,您的echo服务器从已在进行的传输中接收数据(正常),但随后在连接上发出新的写入请求(不正常)。我怀疑此写入操作会导致echo服务器提前退出而不读取所有数据。或者:

    • 客户端关闭连接是因为它接收到了不需要的数据,或者
    • echo服务器在上引发未捕获的异常 tClient.GetStream().WriteByte(pByte);

        2
  •  1
  •   usr    8 年前

    回音服务器坏了。它在每个字节后休眠100毫秒。那样的话,回复你的信息要花很长时间。

    这个 Available 支票总是错的。阅读前不需要检查,也不需要睡觉。检查是否已连接也不起任何作用,因为客户端可以在检查之后立即断开连接。

    我认为这应该管用:

    tClient.GetStream().CopyTo(tClient.GetStream());
    

        3
  •  -1
  •   Craig.Feied    8 年前

    您的代码将异步进程包装在 using 块,因此代码执行自然会继续,到达 使用 Using Async 正如你所看到的,他们并不总是很般配。

    你的 await 将方法的其余部分注册为异步操作的延续,然后它立即返回给调用者。一旦WriteAsync返回,就会执行延续。

    也许您希望WriteAsync只有在成功传输所有字节之后才会返回。这似乎合乎逻辑,但并不正确。WriteAsync在将数据写入流时返回,但这并不意味着数据已通过TCPIP传出。当WriteAsync返回时,流仍将与正在进行的套接字活动一起使用。

    因为你的续集不包含任何内容,所以你退出 使用 立即阻塞,流在仍在使用时被释放。

    所以我认为你的客户机和流没有什么问题,刷新它也没用。考虑到用户的行为,您正从所编写的代码中获得预期的行为 Echo server .

    here

    你可以读另一个同样的问题 here .

    如果你不能控制 Echo Server 使用 --相反,只需实例化客户机和流,然后在一些自然的情况下使用它们,并且 close 当(在你的应用程序后面的某个地方)你确信你已经完成了它们的时候。当然,您将需要某种方法来检测您已经完成了(或者到达某个点,您想关闭它们,无论数据是否在某处成功接收)。

    奥托,如果你想保留 块,然后你必须放一些代码 在你的续集里 为了测试是否完成,所以当WriteAsync将数据传递给套接字并请求关闭套接字时,您不会立即关闭流以进行进一步的通信。

    如果您正在发送非常小的消息,并且确信您知道它们需要多长时间才能被发送或变得过时,那么您可以添加一个手册 await Task.Delay()

    或者可以使用非异步的方法。异步方法一直都是异步的,所以 WriteAsync Stream.BeginWrite ,当然在传输完成之前返回。非异步方法,例如 Write() 将调用或实现非异步流方法,如 Stream.Write() Framework 对于流。

    当然,如果你拥有 回音服务器 然后,您可以查看它为什么不再显示数据时,它不能再与您的网络流通信。

    推荐文章