从尚未关闭的服务器套接字中读取所有字符串

Reading all string from a server socket which has not been closed

我在 Java 中遇到套接字编程问题。 有一个服务器已经用 python 写成这样,我不应该改变它。

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send('from server\nnewline[=10=]')
data = s.recv(BUFFER_SIZE)
s.close()

现在我想在 Java 中编写代码,从服务器读取字符串。像这样:

public static String readStr(Socket client) throws IOException {
        InputStreamReader inStream = new InputStreamReader(
                client.getInputStream());
        BufferedReader inBuff = new BufferedReader(inStream);
        String answer = new String();
        String str = inBuff.readLine();
        while (str!=null) {
            answer = answer.concat(str + "\n");
            str = inBuff.readLine();
        }
        answer = answer.substring(0, answer.length() - 1);
        System.out.println("answer:\n "+answer);
        return answer;
    }

但它似乎在消息的最后一行 str = inBuff.readLine(); 行阻塞。我尝试了 read() 方法,但它也被阻止了。

这可能是因为服务器发送的最后一行没有结束,而 readLine() 方法仅在到达行尾时才 returns。由于您更改了服务器的代码。我建议您使用另一种方法从流中读取。您也可以使用 InputStreamReader class.

在设计基于 tcp 的协议时,最好的方法是包含某种成帧器。这是通过使用 NUL 字节在当前协议中完成的。

从socket中读取数据时,首先要通过一些操作将其分成blocks/frames,然后再解析各个块。

将数据包分成块的粗略方法是读取直到找到 NUL 字节,然后将该块作为字节数组返回。 (这不是最有效的实现)

public byte[] readPacket(InputStream in) throws IOException {
     ByteArrayOutputStream tempStr = new ByteArrayOutputStream();
     int read;
     read=in.read();
     while(read > 0){
       tempStr.write(read);
       read=in.read();
       }
     if(read == -1)
         throw new EOFException();
     return tempStr.toByteArray();
}

因为您的数据现在有了合适的框架,所以您现在可以轻松读取数据:

byte[] frame = readPacket(in);
String[] lines = new String(frame, StandardCharsets.UTF8).split("\n");
// Do something with the lines

除了已经提到的 message/line 结尾不一致 - 一次是 \n,第二次是 [=12=],在服务器上没有检测到消息的结尾。因此,只要客户端未关闭套接字(或关闭写入),服务器就会循环。当你在关闭套接字之前有这一行时:

data = s.recv(BUFFER_SIZE)

换句话说,客户端正在等待服务器的一些响应。但是服务器一直卡在循环中读取来自客户端的消息。

因此您需要在 recv 调用之前关闭套接字或发送一些消息(如空行)并在服务器上检测并最终退出循环。