简体   繁体   English

不完整的消息(C#TCP / IP客户端)

[英]Incomplete messages (C# TCP/IP Client)

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服务器和C#客户端之间进行非常简单的TCP / IP通信。

Java server: 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+"\n"+(char)13));
            os.flush();
            System.out.println("ComInterface: Message <" + msg + "> sent");

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

And here's the C# client: 这是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...

Instead of for instance 而不是例如

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! 非常感谢你!

A message (any data, I mean), when sent through a socket, is divided into several packets . 通过套接字发送的消息(我指的是任何数据)被分成几个包 When printing each received packet, you don't see your whole message. 打印每个收到的数据包时,您看不到整个消息。

You should define an end of message string (something like ".#."). 您应该定义消息字符串的结尾(类似“。#。”)。 Until you receive this sequence, you keep concatenating the messages you receive. 在收到此序列之前,请先将收到的消息串联起来。

This is what session protocols (that is, protocols that run on the top of TCP) do. 这就是会话协议 (即在TCP顶部运行的协议)的作用。

Hope this helps. 希望这可以帮助。

Regards, Calil 问候,卡里尔

TCP is a stream-oriented connection, not message-oriented. TCP是面向流的连接,而不是面向消息的连接。 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. TCP可以自由地将流分解成多个片段,它们将在客户端以这些片段大小的块形式接收。 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. 这样,客户端首先读取了长度前缀,这样便可以知道传入消息应该有多大。

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. 我会认真考虑使用类似Google的协议缓冲区之类的方法来声明您的消息,然后使用size prefix选项流式传输它们。 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. 令人高兴的是,您一次定义了一组消息,然后使用可用的工具根据消息定义自动生成C ++,Java,C#等代码。 This will help in having a consistent messaging set that works between languages. 这将有助于在各种语言之间使用一致的消息传递集。

Take a look at this example... 看一下这个例子...

Java TCP Server... Java TCP服务器...

import java.net.*;
import java.io.*;

public class TcpServer
{
    public static void main(String h[])
    {
        try
        {
            ServerSocket serverSocket = new ServerSocket(1100);
            Socket socket = serverSocket.accept();
            System.out.println("Client Accepted");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("Received: " + bufferedReader.readLine());
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            printWriter.println("Hello Theo. Welcome to socket programming.");
        } catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

C# TCP Client... C#TCP客户端...

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

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var client = new TcpClient("localhost", 1100);
            var stream = client.GetStream();
            var streamWriter = new StreamWriter(stream);
            streamWriter.WriteLine("My name is Theo");
            streamWriter.Flush();
            var streamReader = new StreamReader(stream);
            Console.WriteLine("Received: " + streamReader.ReadLine());
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        Console.WriteLine("Press a key to continue.");
        Console.ReadKey();
    }
}

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

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