You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift转C#:TCP连接StreamReader无法持续读取服务端消息

解决C# TCP流仅能读取首条回复的问题

我来帮你拆解下问题核心:你的Swift代码能持续捕获TCP流的所有消息,但C#用StreamReader的方法只能拿到第一条,本质是TCP流处理逻辑不匹配,以及StreamReader/StreamWriter的默认行为不符合你的服务端推送场景。

问题根源分析

  1. 发送命令时的额外字符:Swift代码是直接发送*99*1##的原始UTF-8字节,但C#的sw.WriteLine(command)会自动在命令末尾添加\r\n换行符。虽然服务端回复了第一条消息,但这种额外字符可能干扰服务端后续的消息推送逻辑(至少不符合协议的精确要求)。
  2. StreamReader方法不适合无边界流:TCP是无消息边界的字节流,ReadToEnd()会一直阻塞到流关闭(服务端断开连接),而你的服务端是持续发送消息不关闭连接。你第一次调用ReadToEnd()时刚好读到了第一条消息,但后续调用时,要么因为服务端未发新消息而无限阻塞,要么被误判为流已结束。
  3. 缺乏逐缓冲的循环读取逻辑:Swift代码是循环读取固定大小的缓冲,每次读到数据就处理,这才是TCP流持续读取的正确姿势;而C#代码依赖ReadToEnd()这种“一次性读到底”的方法,完全不匹配服务端的持续推送模式。

解决方案:模仿Swift的原始字节读取逻辑

最可靠的方式是直接使用NetworkStream读取字节,和Swift的代码逻辑完全对齐,避免StreamReader的缓冲和格式干扰。

完整代码示例

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

class TcpClientDemo
{
    static void Main()
    {
        string address = "192.168.0.10";
        int port = 20000;
        // 使用using语句自动管理资源,避免连接/流泄漏
        using (TcpClient client = new TcpClient(address, port))
        using (NetworkStream stream = client.GetStream())
        {
            // 发送命令:和Swift一致,直接发送原始字节,不添加任何额外字符
            string command = "*99*1##";
            byte[] commandBytes = Encoding.UTF8.GetBytes(command);
            Console.WriteLine("Client->" + command);
            stream.Write(commandBytes, 0, commandBytes.Length);

            // 设置接收超时(和服务端30分钟超时保持一致)
            client.ReceiveTimeout = 30 * 60 * 1000;

            // 和Swift使用相同大小的缓冲(可根据你的Swift代码调整buffer大小)
            byte[] buffer = new byte[1024];
            int readBytesCount;

            while (true)
            {
                try
                {
                    // 循环读取流中的字节
                    readBytesCount = stream.Read(buffer, 0, buffer.Length);
                    if (readBytesCount > 0)
                    {
                        // 将读取到的字节转为字符串
                        string receivedMsg = Encoding.UTF8.GetString(buffer, 0, readBytesCount);
                        Console.WriteLine("Server->" + receivedMsg);
                        // 写入日志文件(追加模式)
                        File.AppendAllText("server_response_log.txt", receivedMsg + Environment.NewLine);
                    }
                    else
                    {
                        // 流已关闭,服务端断开连接
                        Console.WriteLine("Server closed connection.");
                        break;
                    }
                }
                catch (IOException ex)
                {
                    // 处理超时或其他IO异常
                    Console.WriteLine("读取异常: " + ex.Message);
                    break;
                }
            }
        }
    }
}

关键调整点说明

  • 发送命令用Write而非WriteLine:确保发送的字节和Swift完全一致,没有额外的换行符,严格符合服务端的协议要求。
  • 使用NetworkStream直接读取字节:避免StreamReader的内部缓冲和字符边界处理,和Swift的读取逻辑完全对齐,能持续捕获服务端推送的所有消息。
  • 设置合理的接收超时:和服务端的30分钟超时保持一致,避免无限等待。
  • 循环读取缓冲:每次读取固定大小的字节,读取到数据就处理,这是TCP流持续读取的标准模式。
  • 资源自动管理:用using语句包裹TcpClient和NetworkStream,确保连接和流被正确释放。

备选方案:如果一定要用StreamReader

如果你坚持使用StreamReader,需要禁用不必要的缓冲,并用循环读取字符的方式处理,同时避免ReadToEnd()/ReadLine()这类依赖边界的方法:

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;

class TcpClientWithStreamReader
{
    static void Main()
    {
        string address = "192.168.0.10";
        int port = 20000;
        using (TcpClient client = new TcpClient(address, port))
        using (NetworkStream stream = client.GetStream())
        // 禁用自动编码检测,设置缓冲大小,leaveOpen=true避免提前关闭流
        using (StreamReader sr = new StreamReader(stream, Encoding.UTF8, false, 1024, true))
        using (StreamWriter sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
        {
            string command = "*99*1##";
            Console.WriteLine("Client->" + command);
            sw.Write(command); // 不要用WriteLine
            sw.Flush();

            char[] charBuffer = new char[1024];
            int readCharsCount;
            while (true)
            {
                try
                {
                    readCharsCount = sr.Read(charBuffer, 0, charBuffer.Length);
                    if (readCharsCount > 0)
                    {
                        string receivedMsg = new string(charBuffer, 0, readCharsCount);
                        Console.WriteLine("Server->" + receivedMsg);
                        File.AppendAllText("server_response_log.txt", receivedMsg + Environment.NewLine);
                    }
                    else
                    {
                        Console.WriteLine("Server closed connection.");
                        break;
                    }
                }
                catch (IOException ex)
                {
                    Console.WriteLine("读取异常: " + ex.Message);
                    break;
                }
            }
        }
    }
}

这个方案的核心是用Read()方法循环读取字符,而不是依赖行或流结束符,同时确保StreamWriter发送的命令和Swift一致。

内容的提问来源于stack exchange,提问作者Annah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:52:47