使用.readlines()方法时如何克服“ \ n”

时间:2019-10-17 07:51:32

标签: java network-programming

我正在使用套接字,并且收到来自服务器的响应,该响应是 string

方法 readline() 不断循环,因为我的响应在每个响应末尾都没有/n/r。所以我的程序被卡在那一行。

我如何接收响应,换句话说,如何告诉readline()方法传输已经结束而在响应结束时没有/n/r? 我不能使用read()方法,因为它返回一个int。这里的接收代码

// Get the return message from the server
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);


// lecture de message

String message = br.readLine();

这是iam要求获得的响应“ 162143045875965485214752310109013112500019900008080807812345612345678912500007412589600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000”

3 个答案:

答案 0 :(得分:1)

TCP是一种流协议,这意味着只要连接了套接字,字节就无休止地来自套接字。因此,数据不会分为单独的消息。因此,从TCP套接字读取数据与从二进制文件读取数据非常相似-您不能只逐行读取数据,因为它们都是带有开始和结束位置的数据块。

如果输入在消息之间不包含定界符,例如\n(或某些其他字符或字节序列),则必须采用其他方法确定要读取多少个字节。取决于协议,有几种解决方法。例如,在HTTP中,响应通常具有Content-Length头,以便读者知道此响应何时结束以及下一个响应开始。

如果要实现自己的协议,一种简单的方法是为每个消息添加一个int前缀,指定其包含的字节数。在这种情况下,读者所要做的就是读取int,从套接字读取适当数量的字节,解析消息,然后读取下一个int ...

另一种方法是使用固定大小的消息,并且每次仅读取固定数量的字节。第三种方法是使用定界符,例如\n或其他未出现在协议有效负载中的字节序列。

如果知道要读取的字节数,请首先创建一个缓冲区以将消息写入其中。假设我们要精确读取500个字节。分配消息缓冲区:

byte messageBuffer[] = new byte[500];

现在我们需要从套接字读取直到满足以下两个条件之一:

  • 消息缓冲区中有500个字节
  • 或者插座已关闭

方便地,每次我们在套接字read上调用InputStream时,我们都会获得已读取的字节数,如果流已结束,则返回-1。因此,我们可以读入消息缓冲区,直到我们用500个字节填充消息缓冲区,或者从-1调用中获得read()

我们最终遇到这样的循环:

int bytesToRead = 500;
InputStream in = socket.getInputStream();
byte messageBuffer[] = new byte[bytesToRead];
for (int readOffset = 0, readBytes = 0; (readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset)) != -1
        && readOffset < bytesToRead;) {
    readOffset += readBytes;
}

或者,如果您愿意,也可以这样:

int readBytes = 0;
int readOffset = 0;
while (true) {
    readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset);
    if (readBytes == -1) {
        break;
    }
    readOffset += readBytes;
}

请注意,我尚未测试此代码。

一旦您已将足够的字节读入缓冲区,如果要从中取出String,则要使用new String(messageBuffer)或类似new String(messageBuffer, Charset.forName("UTF-8"))之类的字元即可非默认字符集。

答案 1 :(得分:0)

我认为您在执行类似操作时遇到此问题

String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);

try (
    Socket socket = new Socket(hostName, portNumber);
    BufferedReader in =
        new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
    String line = in.readLine();
)
...

根据Javadoc for BufferedReader#readLine

  

行被认为由换行符('\ n'),回车符('\ r'),回车符后紧跟换行符或到达末尾中的任何一条来终止。文件(EOF)。

因此预期会出现此问题。 我建议开始使用行终止符。如果无法执行此操作,则可以使用BufferedReader#read方法并根据规则手动解析收到的内容。

答案 2 :(得分:0)

您的问题似乎是您正在尝试使用readline()来读取非面向行的数据。如果信息流由一系列消息组成,并且您需要一次阅读它们,那么它将无法正常工作。

你做什么?

这取决于实际消息的结构。例如:

  • 如果每则消息始终完全由N个字符组成:

    StringBuilder sb = new StringBuilder(N);
    for (int i = 0; i < N; i++) {
        int ch = br.read();
        if (ch == -1) { 
             throw new IOException("truncated message");
        }
        sb.append((char) ch));
    }
    return sb.toString();
    
  • 如果每封邮件均以大小开头:

    // read the size
    // read 'size' characters.
    
  • 如果每个消息都以字符C(或流的结尾)结尾

    StringBuilder sb = new StringBuilder();
    int ch;
    while((ch = br.read()) != C) {
       sb.append((char) ch));
    }
    return sb.toString();
    

以此类推。