Java客户端和C++服务器通过TCP套接字发送和接收数据。

6

我有一个C++服务器和两个客户端(Ruby和Java)。 所有东西都运行在64位的Linux机器上(Java 1.7.0_17)。 Ruby客户端完全正常,但是Java版本出现了问题。

在Java中,我尝试从客户端向服务器发送一个字符串。实际上,服务器接收到了整个字符串,但是服务器认为还有更多内容需要接收。

Ruby客户端看起来有点像这样:

socket = TCPSocket.open(@options[:host],@options[:port])
test = "Hello, World"
socket.puts test
socket.shutdown 1
response = socket.gets

这里的一切都正常工作。Ruby客户端发送一个字符串,服务器接收该字符串并回复。

Java版本如下:

String ip = "127.0.0.1";
int port = 6686;
java.net.Socket socket = new java.net.Socket(ip,port);
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
InputStreamReader in = new InputStreamReader(socket.getInputStream());

String msg = "Hello, world!";

//send
PrintWriter pw = new PrintWriter(out, true);
pw.print(msg);
pw.flush();
// I also tried: out.write(msg); out.flush(); nothing changed

//receive the reply
BufferedReader br = new BufferedReader(in);
char[] buffer = new char[300];
int count = br.read(buffer, 0, 300);
String reply = new String(buffer, 0, count);
System.out.println(reply);

socket.close();

另一方面,有一个C++服务器:

string receive(int SocketFD) {
   char buffer[SOCKET_BUFFER_SIZE];
   int recv_count;

   // empty messagestring
   string message = "";

   // empty buffer
   memset(buffer, 0, sizeof(buffer));

   while ((recv_count = recv(SocketFD, buffer, sizeof(buffer) - 1, 0)) > 0) {
      /*if (recv_count == -1) {
         cout << "failed." << endl;
         break;
      }*/
      cout << recv_count << endl;
      if (ECHO_SOCKETS) cout << "received: " << buffer << endl;

      message.append(buffer);
      memset(buffer, 0, sizeof(buffer));

      if (ECHO_SOCKETS) cout << "message is now: " << message << endl;

   }
   return message;
}

Java消息的服务器输出为:

13
received: Hello, world!
message is now: Hello, world!

然后什么也没有发生。 问题在于:

recv(SocketFD, buffer, sizeof(buffer) - 1, 0)

如果程序陷入无限循环(或类似情况),则可以终止Java客户端进程,或输入如下内容:

pw.print(msg);
out.close();

服务器端的输出为:
_sending reply: "Request unrecognized/invalid" request="Hello, world!"
send reply success
now close connection

这个输出是正确的(除了“发送回复成功”),但如果添加以下内容:
out.close();

客户端无法接收服务器的回复,因为Socket已关闭。

java.net.SocketException: Socket is closed
at java.net.Socket.getInputStream(Socket.java:864)
at MyServer.writeMessage(MyServer.java:56)
at MyServer.test(MyServer.java:42)
at MyServer.main(MyServer.java:30)

编辑

我尝试了调用 pw.flush() 以及不同的分隔符如 "\n"、"\r"、"\r\n" 和 "\n\r",但服务器仍然认为还有数据需要读取。我还尝试使用 DatagramSockets:

java.net.DatagramSocket dSocket = new java.net.DatagramSocket();
InetAddress address = InetAddress.getByName("localhost");
String msg = "Hello, world!";
byte[] buf = msg.getBytes();
java.net.DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 6686);

但是服务器无法接受数据包。

解决方案

ruby-client在调用puts后会执行像socket.shutdownOutput()这样的操作(ruby: socket.shutdown 1)。我修改了java-client代码:

out.write(msg);
socket.shutdownOutput();

而且它还可以正常工作!

就像@Charly所说的那样:我必须定义一个“协议”。在我的情况下,我不能改变任何与通信相关的代码(服务器和ruby客户端)因为这个功能被另一组研究人员使用。因此,我必须修改我的java客户端,使其在确切相同的时间内执行确切相同的操作,就像一个协议一样。

3个回答

2

当自动刷新为true时,PrintWriter缓冲区只有在调用println或printf时才会被刷新。调用print可能不会刷新缓冲区 (Javadoc)。尝试调用println或直接使用OutputStreamWriter并刷新()。

注意使用正确的字符集(可以在OutputStreamWriter构造函数中设置)。


我已经使用了pw.flush(),但是什么也没改变。现在我试图直接使用OutputStreamWriter: out = new OutputStreamWriter(socket.getOutputStream(), Charset.forName("ISO-8859-1")); …和out.write(msg); out.flush(); 但结果相同。 - MartinO.
我修改了Java客户端源代码。 - MartinO.
我真的不懂C++,但是你应该有一种分隔符来指示消息何时结束发送到服务器吧?就我所看到的,你的服务器只有在连接关闭时才会发送回显...例如,在Java中BufferedReader的readLine()只有在读取到\n或\r时才会返回。 - Charly
我已经尝试过类似于:"Hello, world!\n"的东西,但服务器会将所有内容打印出来并停留在同一行。正如我在帖子末尾提到的那样,我认为我需要一个特殊的分隔符,但为什么Ruby可以工作呢?当然,我不想根据客户端使用的编程语言编写服务器代码。 - MartinO.
当然,你不能根据客户端使用的编程语言编写服务器。两者都必须兼容,这就是为什么需要在两者之间定义一种“协议”的原因。如果您决定客户端必须发送一个字符串和“\n”,那么在Ruby和Java中也是如此。在您的C++代码中,只需测试此符号何时出现,您就可以退出循环并发送回复。关于Ruby,我不太清楚,但似乎“puts”在末尾写入换行序列。 - Charly
所以,puts会换行。我尝试了"\n"、"\r"、"\n\r"和"\r\n",但都没有帮助。但是我意识到ruby客户端在调用puts后会执行类似于socket.shutdownOutput()的操作(ruby:socket.shutdown 1)。我改变了java客户端代码:out.write(msg);socket.shutdownOutput(); 然后它就可以工作了! - MartinO.

0

分别关闭流并使用以下方式刷新:

DataOutputStream dataOut = new DataOutputStream(socket.getOutputStream());
dataOut.writeUTF(s);
dataOut.flush();

嗨 Misch,DataOutputStream,writeUTF(msg)和flush()无法工作。 服务器上的recv_count为15! (我将接受13或14)。输出:cout <<“received:”<< buffer << endl;为空。没有字符。 - MartinO.

0
while ((recv_count = recv(SocketFD, buffer, sizeof(buffer) - 1, 0)) > 0) {
      if (recv_count == -1) {

我不知道你的问题是什么,但这段代码肯定是胡说八道的。内部测试永远不可能成功。


你是正确的。它只是一个旧的代码片段。你可以忽略它。 - MartinO.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接