不完整的消息(C# TCP/IP 客户端) [英] Incomplete messages (C# TCP/IP Client)

查看:23
本文介绍了不完整的消息(C# TCP/IP 客户端)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我绝对不是网络程序员.我尝试做的是在 Java 服务器和 C# 客户端之间进行非常简单的 TCP/IP 通信.

First of all, I'm absolutely not a network programmer. What I try to do, is a very simple TCP/IP communication between a Java server and a C# client.

Java 服务器:

 public void run(){   
try {
       // Open server socket
       _server = new ServerSocket(SERVER_PORT);
       _client = _server.accept();
                System.out.println("ComInterface: client connected.");
                // Wait for a client data output stream
                while(true){

                    // Receive message from client
                    BufferedReader is =
                            new BufferedReader(new InputStreamReader(_client.getInputStream()));
                    msg = is.readLine();

                    // Process message
                    if(msg!=null){
                        System.out.println("ComInterface: Message Received : <" + msg + ">.");
                        processMessage(msg); // Independant method
                    }
                    else{
                        System.out.println("ComInterface: client closed connection.");
                        _client.close();
                        _client = _server.accept();
                        System.out.println("ComInterface: client connected.");
                    }

                }

            } catch (IOException e) {
                e.printStackTrace();
            }
}

public void sendMessage(String msg){
        try {

            // Out stream
            DataOutputStream os = new DataOutputStream(_client.getOutputStream());

            os.writeBytes((String)(msg+"
"+(char)13));
            os.flush();
            System.out.println("ComInterface: Message <" + msg + "> sent");

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }

这是 C# 客户端:

public class ComInterface : MonoBehaviour
    {
        public const String SERVER_IP = "127.0.0.1"; // Localhost
        public const int PORT = 1100; // Default port 
        public const int READ_BUFFER_SIZE = 5000; // 4.8828125 kilobytes

        private TcpClient _client;
        private ASCIIEncoding _asen;
        private byte[] _readBuffer;
        private String _msg;

        public Boolean connected { get; internal set; } // setter is for internal use only

        /**
         * Initialize internal variables (buffer, socket...)
         */
        public ComInterface()
        {
            connected = false;
            _client = new TcpClient();
            _asen = new ASCIIEncoding();
            _readBuffer = new Byte[READ_BUFFER_SIZE];
            _msg = String.Empty;
        }

        /**
         * Connect to server at SERVER_IP:PORT
         * Return true if connection was a success, or false if failure.
         */
        public Boolean Connect()
        {
            try
            {

                _client.Connect(SERVER_IP, PORT);
                connected = true;
                Array.Clear(_readBuffer, 0, _readBuffer.Length);
                Debug.Log("TCPClient: <Connect> Connected to the server");
                // Start an asynchronous read invoking ReceiveComMessage
                _client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _client.GetStream());
            }
            catch (Exception ex)
            {
                Debug.Log("TCPClient: <Connect> Cannot connect to the server - " + ex.Message);
                connected = false;
            }
            // Return connection state
            return connected;
        }

 /**
         * Received a message from Communicator
         */
        private void ReceiveComMessage(IAsyncResult ar)
        {
            int BytesRead;
            String msg;
            try
            {
                BytesRead = _client.GetStream().EndRead(ar);
                if (BytesRead < 1)
                {
                    // if no bytes were read server has close.  
                    Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (BytesRead<1)");
                    this.Disconnect();
                    return;
                }
                // Convert the byte array the message was saved into,
                msg = Encoding.ASCII.GetString(_readBuffer);
                Debug.Log("C# Message: "" + msg + """); // Output example in log below
                BytesRead = 0;
                Array.Clear(_readBuffer, 0, _readBuffer.Length);

                // Start a new asynchronous read into readBuffer.
                _client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _readBuffer);

            }
            catch (Exception ex)
            {
                Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (Exception):" + ex.Message + " see " + ex.StackTrace);
                this.Disconnect();
            }

主要问题是所有到达的消息都不完整.这是日志跟踪:

The main problem is that all the message are arriving incomplete. Here's the log trace:

C#: Message "{
C#: Message ""sender":"Bob"",
C#: Message ""recipient":",
etc...

而不是例如

C#: Message "{"sender":"Bob","recipient":[1,2,3]}"

我有点困惑,我需要一些帮助来解决这个问题.非常感谢!

I'm a bit confused and I'd need some help to resolve this. Thank you very much!

推荐答案

TCP is a stream面向连接,而不是面向消息.它没有消息的概念.当您写出序列化字符串时,它只会看到无意义的字节序列.TCP 可以自由地将该流分解为多个片段,并且它们将在客户端以这些片段大小的块接收.您可以在另一端重建整个消息.

TCP is a stream-oriented connection, not message-oriented. It has no concept of a message. When you write out your serialized string, it only sees a meaningless sequence of bytes. TCP is free to break up that stream up into multiple fragments and they will be received at the client in those fragment-sized chunks. It is up to you to reconstruct the entire message on the other end.

在您的场景中,通常会发送消息长度前缀.这样,客户端首先读取长度前缀,以便它可以知道传入的消息应该有多大.

In your scenario, one would typically send a message length prefix. This way, the client first reads the length prefix so it can then know how large the incoming message is supposed to be.

我会认真考虑使用类似 Google 的 Protocol Buffers 作为声明您的消息,然后使用大小前缀选项流式传输它们.好消息是您定义了一组消息,然后使用可用的工具 从消息定义中自动生成 C++、Java、C# 等代码.这将有助于在语言之间使用一致的消息传递集.

I would seriously consider using something like Google's Protocol Buffers as a good way of declaring your messages and then streaming them with the size prefix option. The nice thing is that you define your set of messages once and then use the available tools to automatically generate C++, Java, C#, etc code from the message definitions. This will help in having a consistent messaging set that works between languages.

这篇关于不完整的消息(C# TCP/IP 客户端)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆