当前位置: 首页 > 知识库问答 >
问题:

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

萧秋月
2023-03-14
  1. 客户端向服务器发送消息1(客户端可以是.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();
            }
        }
    }
}
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();
            }
        }
    }
}
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();
        }
    }
}
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();
            }
        }
    }
}

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

共有1个答案

杨雪松
2023-03-14

不要使用NetworkStream.DataAvailable在消息的开头追加数据的大小。例如,邮件的长度为12501使用前4个字节作为邮件长度。

首先定义一个从缓冲区读取数据的方法

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();
 类似资料:
  • 我有一个TCP套接字客户端(使用Socket类),它连接到一个简单的TCP套接字服务器(仅用于测试)我有以下代码段: 我还有一个检查连接状态并尝试重新连接的过程...在尝试重新连接到一个新的套接字并读取数据后,我在这一行收到一个消息错误:byteRead=asocket.receive(dataByte)‘number of bytes“一个现有的连接被远程主机强制关闭了” 我已经执行了一些搜索在

  • tcp 服务端 tcp 服务端 源码/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * *//** 程序清单:tcp 服务端 * * 这是一个 tcp 服务端的例程 * 导出 tc

  • 创建 TCP 服务器 使用最简单的方法来创建一个 TCP 服务器,使用所有默认选项如下所示: NetServer server = vertx.createNetServer(); 配置 TCP 服务器 如果你不想默认值,可以将服务器配置通过传入一个NetServerOptions实例来创建它: NetServerOptions options = new NetServerOptions().s

  • 未来你可能会用 Redis 服务器或者其他的消息队列系统来作为 logstash broker 的角色。不过 Logstash 其实也有自己的 TCP/UDP 插件,在临时任务的时候,也算能用,尤其是测试环境。 小贴士:虽然 LogStash::Inputs::TCP 用 Ruby 的 Socket 和 OpenSSL 库实现了高级的 SSL 功能,但 Logstash 本身只能在 SizedQu

  • ?> Swoole\Coroutine\Server 是一个完全协程化的类,用于创建协程TCP服务器,支持TCP和unixSocket类型。 与Server模块不同之处: 动态创建销毁,在运行时可以动态监听端口,也可以动态关闭服务器 处理连接的过程是完全同步的,程序可以顺序处理Connect、Receive、Close事件 !> 在4.4以上版本中可用 短命名 可使用Co\Server短名。 方法

  • 程序代码 server.php //创建Server对象,监听 127.0.0.1:9501端口 $serv = new Swoole\Server("127.0.0.1", 9501); //监听连接进入事件 $serv->on('Connect', function ($serv, $fd) { echo "Client: Connect.\n"; }); //监听数据接收事