C# - StreamReader.ReadLine无法正常工作!

11

我一直在尝试实现Java中的BufferedReader做的事情。我打开了一个套接字流,只想以逐行的方式读取它。

以下是我的服务器代码:

while (continueProcess)
        {
            try
            {
                StreamReader reader = new StreamReader(Socket.GetStream(), Encoding.UTF8);
                string command = reader.ReadLine();
                if (command == null)
                    break;

                OnClientExecute(command);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

以下是客户端代码:

TcpClient tcpClient = new TcpClient();
        try
        {
            tcpClient.Connect("localhost", serverPort);
            StreamWriter writer = new StreamWriter(tcpClient.GetStream(), Encoding.UTF8);
            writer.AutoFlush = true;
            writer.WriteLine("login>user,pass");
            writer.WriteLine("print>param1,param2,param3");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
        finally
        {
            tcpClient.Close();
        }

服务器只读取第一行 (login>user,pass),然后 ReadLine 返回 null!

如何最简便地实现像Java中的 BufferedStreamReader 一样的按行读取器?😞


1
请在这里处理您的对象(StreamReader和StreamWriter)。否则,您可能会遇到意外问题。只需在它们周围放置一个using块(例如using (StreamWriter writer = ...) { }),您就可以了。 - configurator
并不是这与问题有关 - 只是一个建议。 - configurator
4个回答

17

一个典型的行阅读器通常是这样的:

using(StreamReader reader = new StreamReader(Socket.GetStream(), Encoding.UTF8)) {
    string line;
    while((line = reader.ReadLine()) != null) {
        // do something with line
    }
}

请注意代码中的using语句,它确保即使我们遇到错误,也能执行Dispose()方法并退出循环。

如果需要的话,您可以通过迭代器块将其抽象出来(关注点分离):

static IEnumerable<string> ReadLines(Stream source, Encoding encoding) {
    using(StreamReader reader = new StreamReader(source, encoding)) {
        string line;
        while((line = reader.ReadLine()) != null) {
            yield return line;
        }
    }
}

(注意我们将其移入函数并删除了"do something",用"yield return"代替,这会创建一个迭代器(一种惰性迭代、非缓存状态机))

然后我们可以这样简单地消耗它:

foreach(string line in ReadLines(Socket.GetStream(), Encoding.UTF8)) {
    // do something with line
}

现在,我们的处理代码不需要担心怎样读取行 - 只需给定一系列的行,就可以对它们进行处理。

请注意,usingDispose())也适用于 TcpClient;你应该养成检查IDisposable的习惯;例如(仍包括错误记录):

using(TcpClient tcpClient = new TcpClient()) {
    try {
       tcpClient.Connect("localhost", serverPort);
       StreamWriter writer = new StreamWriter(tcpClient.GetStream(), Encoding.UTF8);
       writer.AutoFlush = true;
       writer.WriteLine("login>user,pass");
       writer.WriteLine("print>param1,param2,param3");
    } catch (Exception ex) {
        Console.Error.WriteLine(ex.ToString());
    }
}

当我使用StreamReader从套接字读取数据时,我发现客户端程序会冻结,在ReadLine()方法处花费无限的时间,最终无法读取。 - Masud Rahman
@MasudRahman 这通常意味着:服务器没有发送完整的行(即您要求它等待服务器没有理由向您发送的内容),或者服务器忘记刷新输出缓冲区。您可以添加读取超时,但最终它无法使服务器发送数据。当然,您可以自己进行阅读和缓冲,但这只是改变了问题的性质 - 然后您可以在一段时间后决定是否发送了完整的行。 - Marc Gravell
原始代码的问题(因为它实际上没有在任何地方解释),就是你每次循环都重新创建了StreamReader,这样会丢失缓冲区中的其余内容。 - Chris Pugmire
原始代码的问题(因为它实际上没有在任何地方解释),就是你在每次循环中重新创建了StreamReader,这样会丢失缓冲区的其余部分。 - undefined

6

您的服务器代码中的 while 循环仅设置为每个连接读取一行。您需要在 try 中再添加一个 while 循环以读取所有被发送的行。我认为一旦客户端侧设置了该流,它将发送所有数据。然后在服务器端,您的流实际上只从该特定流中读取一行。


修改了我的服务器和客户端代码如下: 服务器: using (StreamReader reader = new StreamReader(Socket.GetStream(), Encoding.UTF8)) { while (continueProcess) ....客户端: ... Thread.Sleep(10000); writer.WriteLine("print>test,one,two"); 我在客户端代码中插入了Thread.Sleep(10000),然后服务器开始抛出异常。客户端套接字将一直保持打开状态,因为客户端将在其生命周期内发送数据,所以似乎这段代码会抛出错误。你不这么认为吗? - Aleyna
没有看到新代码或新异常,我不确定问题可能是什么。 - LJM

0
    public string READS()
    {
        byte[] buf = new byte[CLI.Available];//set buffer
        CLI.Receive(buf);//read bytes from stream
        string line = UTF8Encoding.UTF8.GetString(buf);//get string from bytes
        return line;//return string from bytes
    }
    public void WRITES(string text)
    {
        byte[] buf = UTF8Encoding.UTF8.GetBytes(text);//get bytes of text
        CLI.Send(buf);//send bytes
    }

CLI 是一个套接字。 由于某些原因,TcpClient 类在我的电脑上不再正常工作, 但 Socket 类仍然可以正常使用。

UTF-8 是标准的 StreamReader / Writer 编码。


-2

尝试过这个,但是出现了以下错误:

找不到类型或命名空间名称“Stream”(是否缺少 using 指令或程序集引用?) 找不到类型或命名空间名称“StreamReader”(是否缺少 using 指令或程序集引用?) 找不到类型或命名空间名称“StreamReader”(是否缺少 using 指令或程序集引用?) “System.Net.Sockets.Socket”不包含“GetStream”的定义


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接