首页 > 解决方案 > 让 TcpClient 等到数据写入

问题描述

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

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:从控制台实用程序调用:

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.");
}

Upd2:Echo 服务器代码(从一些 stackoverflow 问题中被盗):

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);
    }
}

标签: c#.nettcpclient

解决方案


简单的部分是回显服务器工作缓慢,因为它在每次读取后暂停 100 毫秒。我想这是为了让你有机会看看发生了什么。

为什么你没有看到所有的数据,我不确定,但我认为可能发生的是:

  • 当您的客户端执行离开using块时,流被处理(感谢 Craig.Feied 在他的回答中指出执行在底层套接字完成数据的物理传输之前继续进行)
  • 处理NetworkStream导致它向底层证券发出关闭的原因Socket
  • 关闭使 有机会Socket在最终关闭之前完成发送任何缓冲数据。参考:Graceful Shutdown、Linger Options 和 Socket Closure
  • 请注意,它本身没有缓冲数据,NetworkStream因为它将所有写入直接传递到套接字。因此,即使在传输完成之前处理了 NetworkStream,也不会丢失任何数据
  • 关闭中的套接字可以完成现有请求,但不会接受新请求。

因此,您的回声服务器从正在进行的传输中接收数据(好的),但随后在连接上发出新的写入请求(不好)。我怀疑此写入导致回声服务器提前退出而不读取所有数据。任何一个:

  • 客户端关闭连接,因为它收到了它不期望的数据,或者
  • echo 服务器在tClient.GetStream().WriteByte(pByte);

应该很容易检查它是否确实是上述任何一种。


推荐阅读