StreamWriter ReadLine 方法挂起程序

StreamWriter ReadLine method hangs program

我正在尝试让一些基本的多线程在 C# 中工作。我发现没有明确的教程,所以请忍受我的低于标准的代码:

class Program
{
    private static TcpListener listener;
    private static List<TcpClient> clients; 
    static void Main(string[] args)
    {
        listener = new TcpListener(IPAddress.Any, 1337);
        clients = new List<TcpClient>();
        StartListening();
        Console.WriteLine("Accepting clients..");
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }

    static async void StartListening()
    {
        listener.Start();
        while (true)
        {
            clients.Add(await listener.AcceptTcpClientAsync().ConfigureAwait(false));
            Console.WriteLine("Client connected!");
            HandleClient(clients[clients.Count - 1], clients.Count);
        }
    }

    static async void HandleClient(TcpClient c, int number)
    {
        Console.WriteLine($"Getting client #{number}'s handshake..");
        var ns = c.GetStream();
        var sr = new StreamReader(ns);
        var handshake = sr.ReadLine();
        Console.WriteLine("Client {0}'s handshake: {1}", number, handshake);
    }
}

对,所以我想在服务器程序中实现的是:

 1. Accepting client
 2. Receiving handshake, print it to console
 3. Add to the clients list

接受客户端部分有效,但是程序在 HandleClient() 的第一行之后就停止了。我试过等待荒谬的时间(1000 毫秒),即使那样它也没有收到任何东西,也没有抛出异常(即它保持连接)。我在这里做错了什么?

客户端代码是here如果你需要它!

您的客户端代码在这里损坏了 - 您永远不会刷新它:

var sw = new StreamWriter(ns);
Thread.Sleep(1000);
sw.WriteLine(handshake);

不需要 Thread.Sleep 调用,但如果您希望数据立即真正发送到网络层,您应该刷新 StreamWriter:

sw.Flush();

虽然(正如 Stephen Cleary 指出的那样)这并不意味着它一定会 立即发送 ,但有理由期望它会在刷新后 "reasonably soon" 发送。

正如 Jon Skeet 提到的,要确保内容不只是位于缓冲区中,您应该刷新它。

您的客户端应如下所示:

var sw = new StreamWriter(ns);
Thread.Sleep(1000); // let the server catch up
sw.WriteLine(handshake);
sw.Flush();
Console.WriteLine("Staying connected.. Press ESCAPE to exit.");

编辑:忘记提及...Thread.Sleep(1000); 那里...您可以安全地删除它。

I am trying to get some basic multi-threading working in C#.

我总是对尝试通过编写裸机 TCP/IP 服务器来学习并发的开发人员感到惊讶(当他们以前从未编程过 TCP/IP 时)。并发和 TCP/IP 是两个极其困难的话题;试图同时学习它们几乎肯定会以灾难告终。这么普遍的方法真的很奇怪。

如果你想学习异步,我推荐我的 async intro and article on best practices(其中,除其他外,你将学习原理 "avoid async void")。

如果你想学TCP/IP,推荐我的TCP/IP .NET FAQ blog series。但是,编写 TCP/IP 代码几乎从来 都不是必需的 ,并且由于您可以同时更改客户端和服务器,因此我 强烈 推荐使用 SignalR相反。

我怀疑您真的想学习 async,而 TCP/IP 只是碍事。因此,放弃较难的主题 (TCP/IP) 并使用 async 更简单的通信,例如 WebAPI 或 SignalR。

(就你看到的实际问题来看,确实是缓冲引起的,但需要注意的是,刷新缓冲区并不会导致数据立即通过网络发送;它只是发送到立即联网。适当的解决方案需要消息框架。)