简体   繁体   English

如何通过 TCP 向带有节点和 C# 的客户端发送简单的 UInt

[英]How to send a simple UInt over TCP to Client with Node and C#

I am using a Node server to send a simple 16 bit Int to my client in Unity.我正在使用 Node 服务器向 Unity 中的客户端发送一个简单的 16 位 Int。 The Unity client has a C# client script to connect to the node server and read the value. Unity 客户端有一个 C# 客户端脚本连接到节点服务器并读取值。

The code I am using passes a String so I am converting the Int to a String and then back again on the client.我正在使用的代码传递一个字符串,因此我将 Int 转换为字符串,然后再次返回客户端。 I would rather just pass the value as a UInt16... firstly, is this more efficient?我宁愿只是将值作为 UInt16 传递......首先,这更有效吗? and how do I do pass it and convert it on the client?以及如何传递它并在客户端上转换它?

Here is the node code:这是节点代码:

server.on('connection', function(socket) {
    console.log('A new connection has been established.');

    // Now that a TCP connection has been established, the server can send data to
    // the client by writing to its socket.
    
    if (sensor != null){
        sensor.on("change", value => {
            socket.write(value.toString());
        });
    }

    // The server can also receive data from the client by reading from its socket.
    socket.on('data', function(chunk) {
        console.log(`Data received from client: ${chunk.toString()}`);
    });

    // When the client requests to end the TCP connection with the server, the server
    // ends the connection.
    socket.on('end', function() {
        console.log('Closing connection with the client');
    });

    // Don't forget to catch error, for your own sake.
    socket.on('error', function(err) {
        console.log(`Error: ${err}`);
    });
});

and this is the C# code in Unity:这是 Unity 中的C#代码:

socketConnection = new TcpClient("localhost", 8080);
            Byte[] bytes = new Byte[1024];
            while (true)
            {
                using (NetworkStream stream = socketConnection.GetStream())
                {
                    int length;
                    // Read incoming stream into byte array
                    while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        var incomingData = new byte[length];

                        //Debug.Log(incomingData.GetValue(0));

                        //int value = Convert.ToUInt16(incomingData);
                        //Debug.Log(value);

                        Array.Copy(bytes, 0, incomingData, 0, length);
                        // Convert byte array to string message.                        
                        string serverMessage = Encoding.ASCII.GetString(incomingData);
                        int value = Int16.Parse(serverMessage);
                        Debug.Log(value);
                    }
                }
            }

I need to do some refactoring but generally, this works to pass a String .我需要做一些重构,但一般来说,这可以传递一个String Passing the UInt16 has not worked.通过UInt16无效。 Any help greatly appreciated.非常感谢任何帮助。

I would rather just pass the value as a UInt16... firstly, is this more efficient?我宁愿只是将值作为 UInt16 传递......首先,这更有效吗?

It is - but there are many advantages to using text-based protocols instead of binary protocols: namely, it's MUCH easier to debug and inspect text-based protocols than binary protocols.确实如此 - 但是使用基于文本的协议而不是二进制协议有很多优点:也就是说,调试和检查基于文本的协议比二进制协议容易得多。 HTTP is text-based, for example (HTTP/2 is a binary protocol, but only because Google wanted to hyper-optimize it which makes sense at their scale, but for the vast majority of applications using a text-based protocol makes a lot more sense).例如,HTTP 是基于文本的(HTTP/2一种二进制协议,但这只是因为 Google 想要对其进行超优化,这在其规模上是有意义的,但是对于使用基于文本的协议的绝大多数应用程序来说更有意义)。

and how do I do pass it and convert it on the client?以及如何传递它并在客户端上转换它?

With difficulty:有困难:

  • It's harder to work with specific numeric types in JavaScript because JavaScript has only the number type which isn't even an integer type.在 JavaScript 中使用特定的数字类型更难,因为 JavaScript 只有number类型,甚至不是 integer 类型。 JavaScript [does have typed buffers][1] but that's an advanced topic. JavaScript [确实有类型缓冲区][1] 但这是一个高级主题。

  • When dealing with multi-byte binary integers you need to deal with "network-ordering" of multi-byte values, also known as endianness .在处理多字节二进制整数时,您需要处理多字节值的“网络排序”,也称为字节序 Most C#.NET environments are "little-endian" but convention is to always use big-endian format on the wire .大多数 C#.NET 环境都是“little-endian”,但惯例是始终在线路上使用 big-endian 格式。

  • TCP only sends packets when the system feels it's optimal (because there's a lot of overhead for each TCP packet), but by default sending individual 2-byte values won't actually be sent until the system has buffered up a lot of 2-byte values - otherwise you'll have to force-flush the network-stack. TCP 仅在系统认为最佳时发送数据包(因为每个 TCP 数据包有很多开销),但默认情况下,在系统缓冲大量 2 字节之前不会实际发送单独的 2 字节值values - 否则你将不得不强制刷新网络堆栈。 Note that using a text-based protocol has the same problem, but at least with a text-based protocol there's less conceptual wastage.请注意,使用基于文本的协议有同样的问题,但至少使用基于文本的协议会减少概念上的浪费。

For sending single 16-bit integer values - you really are better-off sticking with a text protocol.对于发送单个 16 位 integer 值 - 你真的最好坚持使用文本协议。

...but if you insist: ...但如果你坚持:

server.on('connection', function(socket) {
    console.log('A new connection has been established.');

    if (sensor != null){
        sensor.on("change", value => {
            
            if( typeof value !== 'number' ) {
                throw new Error( "value must be a number." );
            }
            if( value < 0 || value > 65535 || Math.floor( value ) !== value ) {
                throw new Error( "value must be a 16-bit unsigned integer." );
            }

            const buffer = new Uint8Array( 2 );

            const hi = ( value >> 8 ) & 0xFF;
            const lo = ( value      ) & 0xFF;
            buffer.set( [ hi, lo ] ); // Big-Endian Order.

            const didFlush = socket.write( buffer );
            if( !didFlush )  console.log('Data did not send immediately.');
        });
    }

    // [...]

});
TcpClient tc = new TcpClient("localhost", 8080); // A TcpClient is not a Socket. It encapsulates a Socket.

Byte[] buffer = new Byte[4096]; // Use 4K as a network buffer size.
while (true)
{
    using( NetworkStream stream = tc.GetStream() )
    {
        Int32 read;
        while( ( read = stream.Read( buffer, 0, buffer.Length ) ) != 0 )
        {
            if( read % 2 != 0 )
            {
                // TODO: Handle the case where a 16-bit value is half-sent.
                continue;
            }
            
            // It is highly unlikely that each packet will contain only 2 bytes, instead it's more likely a sequence of 16-bit integers will be sent all-at-once, so we read each of them in 2-byte steps from the buffer:
            for( Int32 idx = 0; idx < read; idx += 2 )
            {
                Byte hi = buffer[ idx + 0 ];
                Byte lo = buffer[ idx + 1 ];

                UInt16 value = ( hi << 8 ) | ( lo );
                Debug.Log( value );
            }   
        } // while
    } // using
}


  [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM