jjzjj

C# TCP 读取所有数据 - 服务器端问题

coder 2023-09-19 原文

我正在使用 C# v4.0 (.Net Framework v4) 开发一个简单的 TCP 服务器应用程序:

我要完成这两个步骤:

  1. 客户端发送message1给服务器(客户端可以是.net或java应用)
  2. 服务器将 message2 发送回客户端作为对 message1 的响应

我的服务器有问题,它无法正确读取 message1,除非我使用这些不适当的解决方案之一:

1) 使用只有 1 字节缓冲区的 MemoryStream(有效但速度慢):

while (true)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    NetworkStream networkStream = tcpClient.GetStream();
    MemoryStream memoryStream = new MemoryStream();

    int numberOfBytesRead = 0;
    byte[] buffer = new byte[1]; // works but slow in case of big messages

    do
    {
        numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);   
        memoryStream.Write(buffer, 0, numberOfBytesRead);
    } while (networkStream.DataAvailable);

    if (memoryStream.Length > 0)
    {
        string message1 = new StreamReader(memoryStream).ReadToEnd();
        if (message1 == "message1")
        {
            using (StreamWriter streamWriter = new StreamWriter(networkStream))
            {
                string message2 = "message2";
                streamWriter.Write(message2);
                streamWriter.Flush();
            }
        }
    }
}

示例:如果 message1.Length == 12501 并且我使用 1024 的缓冲区,NetworkStream.Read() 循环只读取 2048 字节的 message1,我认为 NetworkStream.DataAvailable 没有返回正确的值!

2) 从 NetworkStream 读取到 Buffer 后使用 Thread.Sleep(1000)(有效但速度慢):

while (true)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    NetworkStream networkStream = tcpClient.GetStream();
    MemoryStream memoryStream = new MemoryStream();

    int numberOfBytesRead = 0;
    byte[] buffer = new byte[8192]; 

    do
    {
        numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);   
        memoryStream.Write(buffer, 0, numberOfBytesRead);
        Thread.Sleep(1000); // works but receiving gets slow
    } while (networkStream.DataAvailable);

    if (memoryStream.Length > 0)
    {
        string message1 = new StreamReader(memoryStream).ReadToEnd();
        if (message1 == "message1")
        {
            using (StreamWriter streamWriter = new StreamWriter(networkStream))
            {
                string message2 = "message2";
                streamWriter.Write(message2);
                streamWriter.Flush();
            }
        }
    }
}

3) 使用 StreamReader.ReadToEnd() 并在发送消息 1 后关闭客户端的套接字(有效但服务器无法使用消息 2 响应客户端):

while (true)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    NetworkStream networkStream = tcpClient.GetStream();
    StreamReader streamReader = new StreamReader(networkStream, true);

    string message1 = streamReader.ReadToEnd(); // blocks until client close its socket

    if (message1 == "message1")
    {
        using (StreamWriter streamWriter = new StreamWriter(networkStream))
        {
            string message2 = "message2";
            streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
            streamWriter.Flush();
        }
    }
}

4) 使用 StreamReader.ReadLine() 循环并关闭客户端的套接字

while (true)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    NetworkStream networkStream = tcpClient.GetStream();
    StreamReader streamReader = new StreamReader(networkStream);

    StringBuilder stringBuilder = new StringBuilder();

    while (!streamReader.EndOfStream)
    {
        stringBuilder.AppendLine(streamReader.ReadLine()); // blocks until client close its socket
    }

    string message1 = stringBuilder.ToString();

    if (message1 == "message1")
    {
        using (StreamWriter streamWriter = new StreamWriter(networkStream))
        {
            string message2 = "message2";
            streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
            streamWriter.Flush();
        }
    }
}

5) 用长度为 message1 加上前缀(可行,但需要客户端向消息添加额外的字节,这不适用于现有的 java 客户端)

while (true)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    NetworkStream networkStream = tcpClient.GetStream();
    MemoryStream memoryStream = new MemoryStream();

    byte[] bufferMessageLength = new byte[4];   // sizeof(int)
    networkStream.Read(bufferMessageLength, 0, bufferMessageLength.Length);  

    int messageLength = BitConverter.ToInt32(bufferMessageLength, 4);

    byte[] bufferMessage = new byte[messageLength]; 
    networkStream.Read(bufferMessage, 0, bufferMessage.Length); 
    memoryStream.Write(buffer, 0, bufferMessage.Length);

    if (memoryStream.Length > 0)
    {
        string message1 = new StreamReader(memoryStream).ReadToEnd();
        if (message1 == "message1")
        {
            using (StreamWriter streamWriter = new StreamWriter(networkStream))
            {
                string message2 = "message2";
                streamWriter.Write(message2);
                streamWriter.Flush();
            }
        }
    }
}

关于这些问题,在不使用上述解决方案的情况下,从客户端读取所有数据的最佳方法是什么?

最佳答案

而不是使用 networkStream.DataAvailable 在消息的开头附加数据的大小。例如,您的消息长度是 12501 使用前 4 个字节作为消息长度。

首先定义一个从buffer中读取数据的方法

public static void ReadStream(NetworkStream reader, byte[] data)
{
    var offset = 0;
    var remaining = data.Length;
    while (remaining > 0)
    {
        var read = reader.Read(data, offset, remaining);
        if (read <= 0)
            throw new EndOfStreamException
                (String.Format("End of stream reached with {0} bytes left to read", remaining));
        remaining -= read;
        offset += read;
    }
}

然后从流中读取数据。

var bytesRead = 0;
var offset = 0;
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
var bufferMessageSize = new byte[4]; // int32

ReadStream(networkStream, bufferMessageSize);

var messageSize = BitConverter.ToInt32(bufferMessageSize, 4); // bytesToRead

var bufferMessage = new byte[messageSize];

ReadStream(networkStream, bufferMessage);


// Now Respond back Client here
// networkStream.Write();

关于C# TCP 读取所有数据 - 服务器端问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38472491/

有关C# TCP 读取所有数据 - 服务器端问题的更多相关文章

  1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  5. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  8. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  9. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  10. ruby - Fast-stemmer 安装问题 - 2

    由于fast-stemmer的问题,我很难安装我想要的任何ruby​​gem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=

随机推荐