TCP communication between C# and Java servers

Giedrius666 :

I am trying to pass a json from C# server to Java server using TCP, problem is that the first time Java server seems to receive an empty json. Second time and onwards it works fine, see output bellow. Any ideas or suggestions are welcome, thanks in advance.

Output:

Starting server...
Waiting for a connection..
Connected!
Reading...
Received empty object
Received: 
Connected!
Reading...
Received: "Request:gethotellist"
Connected!
Reading...
Received: "Request:gethotellist"

Here is the C# code snippet for sending json:

public void GetHotelList()
{
TcpClient clientSocket = new TcpClient();
clientSocket.Connect("127.0.0.1", 6767);

NetworkStream ns = clientSocket.GetStream();

string jsonRequest = "Request:gethotellist";

string jsonToSend = JsonConvert.SerializeObject(jsonRequest);

byte[] dataBytes = Encoding.UTF8.GetBytes(jsonToSend);

ns.Write(dataBytes, 0, dataBytes.Length);

ns.Close();
}

Java server:

public class JHotelServer
{

public static void main(String[] args) throws IOException
{
  final int PORT = 6767;

  System.out.println("Starting server...");     

  @SuppressWarnings("resource")
  ServerSocket welcomeSocket = new ServerSocket(PORT);  

  System.out.println("Waiting for a connection..");

  while(true)
  {             
     try
     {
        Socket connectionSocket = welcomeSocket.accept();  

        System.out.println("Connected!");

        Thread connectionThread = new Thread(new TcpConnectionManager(connectionSocket));
        connectionThread.start();
     }

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

And here is the Tcp Communication manager:

public class TcpConnectionManager implements Runnable
{   
private DataInputStream inFromDotNet;

public TcpConnectionManager(Socket socket) throws IOException
{
  inFromDotNet = new DataInputStream(socket.getInputStream());
}

@Override
public void run()
{
  try
  {               
     System.out.println("Reading...");

     byte[] rvdMsgByte = new byte[inFromDotNet.available()];

     // Collecting data into byte array
     for (int i = 0; i < rvdMsgByte.length; i++)
     {
         rvdMsgByte[i] = inFromDotNet.readByte();
     }

     if (rvdMsgByte.length == 0)
     {
        System.out.println("Received empty object");
     }

     // Converting collected data in byte array into String.
     String rvdMsgTxt = new String(rvdMsgByte);

     System.out.println("Received: " + rvdMsgTxt);       
  }

  catch(IOException ioe)
  {
     ioe.printStackTrace();
  }
}
}
Marc Gravell :

Based on the code shown, it would be entirely expected to sometimes get an empty payload, because if the payload packets haven't arrived yet: inFromDotNet.available() will be zero.

Basically, .available() (and equivalent) should never be used to determine anything about the content, other than "there are bytes currently buffered" (perhaps to choose between sync read and async read).

There are two approaches commonly used here:

  • use EOF to indicate the end of the payload, i.e. read until the socket says it is closed and everything has been read; how you detect this depends on the specific socket API (for example, in .NET, it would be when Receive returns a non-positive number)
  • implement some kind of basic framing protocol

The first option is relevant if you will only ever send one message per socket; the second option is necessary if you will be sending multiple messages per socket. A basic framing protocol could be as simple as "messages are separated by newline characters", but in many cases you may need binary-safe framing, such as a length prefix. For example, your JSON could contain newlines, which could incorrectly be interpreted as the end of a frame.

In either case: with sockets you almost always need to read in a loop, because the data can be split over multiple packets, and/or your receive buffer size may be too small. So usually, you would loop and buffer all the payload until you detect either an EOF or the end of a frame, and only then start trying to process the content. In particular, you should not usually try to decode text until you know you have the entire thing, because multi-byte characters may span multiple "read"/"receive" calls, so: unless you're using a stateful text decoder, you may incorrectly decode such characters.

The behaviour you're seeing would be common to almost all platforms and languages; it isn't specific to Java/C#.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=117164&siteId=1