TCP связь между C # и серверами Java - PullRequest
0 голосов
/ 12 ноября 2018

Я пытаюсь передать json с сервера C # на сервер Java с помощью TCP, проблема в том, что сервер Java впервые получает пустой json.Второй раз и далее он работает нормально, смотрите вывод ниже.Приветствуются любые идеи или предложения, заранее спасибо.

Вывод:

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

Вот фрагмент кода C # для отправки 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-сервер:

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();
     }         
  }
}
}

А вот менеджер связи Tcp:

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();
  }
}
}

1 Ответ

0 голосов
/ 12 ноября 2018

Исходя из показанного кода, вполне ожидаемо иногда получить пустую полезную нагрузку, потому что, если пакеты полезной нагрузки еще не поступили: inFromDotNet.available() будет равно нулю.

По сути, .available() (и эквивалент) следует никогда использовать для определения чего-либо о содержимом, кроме «есть байты, буферизируемые в настоящее время» (возможно, для выбора между синхронизированным чтением и асинхронным чтением).

Здесь обычно используются два подхода:

  • используйте EOF для указания конца полезной нагрузки, то есть читайте до тех пор, пока сокет не скажет, что он закрыт и все прочитано ; как это определить, зависит от конкретного API сокета (например, в .NET это будет, когда Receive возвращает неположительное число)
  • реализовать какой-то базовый протокол кадрирования

Первый вариант актуален, если вы будете отправлять только одно сообщение на сокет; вторая опция необходима , если вы будете отправлять несколько сообщений на сокет. Базовый протокол кадрирования может быть таким простым, как «сообщения разделяются символами новой строки», но во многих случаях вам может потребоваться бинарно-безопасное кадрирование, такое как префикс длины. Например, ваш JSON может содержать символы новой строки, которые могут неправильно интерпретироваться как конец кадра.

В любом случае: с сокетами вам почти всегда необходимо читать в цикле, потому что данные могут быть разбиты на несколько пакетов, и / или размер вашего буфера приема может быть слишком мал. Поэтому обычно вы зацикливаете и буферизируете всю полезную нагрузку, пока не обнаружите EOF или конец кадра, и только после этого начнет пытаться обработать контент. В частности, обычно не следует пытаться декодировать текст, пока вы не узнаете, что у вас есть все, потому что многобайтовые символы могут охватывать несколько вызовов «чтение» / «получение», поэтому: если вы не используете декодер текста с состоянием, вы может неправильно декодировать такие символы.

Поведение, которое вы видите, будет общим практически для всех платформ и языков; это не относится к Java / C #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...