缓冲读取器未从套接字接收数据

5
我正在编写一个客户端应用程序,通过tcp/ip接收连续的数据流。我遇到的问题是缓冲读取器对象没有接收到任何数据,并且在readline方法处停滞不前。
服务器的工作方式是连接到它,然后发送身份验证信息以接收数据。下面是我的代码要点。
socket = new Socket(strHost, port);
authenticate();
inStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
process(inStream);

authenticate()
{      
  PrintWriter pwriter = new PrintWriter(socket.getOutputStream(), true);
  pwriter.println(authString);
}

process(BufferedReader bufferedReader)
{
   while((line = bufferedReader.readLine()) != null)
      dostuff
}

我创建了一个样例服务器应用程序,按照我认为的服务器发送数据的方式发送数据,并成功连接、接收和处理数据。我可以在我的应用程序中成功连接到服务器。我也可以telnet到服务器并写入身份验证字符串,使用telnet接收大量数据。然而,我的应用程序在与服务器进行readLine时却一直挂起,我已经没有任何想法为什么会这样。
通过telnet进来的数据看起来像是以下连续流:
 data;data;data;data;data
 data;data;data;data;data

为什么我的应用程序在readline处卡住了,我没有正确输出认证行吗?我没有收到任何错误信息...

编辑 我的示例服务器代码(正常工作)...再次强调,这只是模拟我认为真实服务器运行的方式,但我可以连接到两个服务器,只是无法从真实服务器接收数据。

  public static void main(String[] args) throws IOException
  {
  ServerSocket serverSocket = null;

  try
  {
     serverSocket = new ServerSocket(1987);
  }
  catch (IOException e)
  {
     System.out.println("Couldn't listen on port: 1987");
     System.exit(-1);
  }

  Socket clientSocket = null;
  try
  {
     clientSocket = serverSocket.accept();
  }
  catch (IOException e) {
     System.out.println("Accept failed: 1987");
     System.exit(-1);
  }

  PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
  BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
  String something;

  while ((something = in.readLine()) != null)
  {
     while(true)
     {
        out.println(message);
     }
  }



  out.close();
  in.close();
  clientSocket.close();
  serverSocket.close();
}

你能展示与问题相关的服务器代码吗? - Coeffect
我可以在我的应用程序中成功连接到服务器。你是指你的虚拟样例服务器吗? - leonbloy
也许在发送认证之前,你应该准备另一个线程来读取数据?我猜服务器一旦收到密码就开始大量发送数据。 - toto2
Mannimarco,我可以展示样例服务器……但我没有连接的服务器代码。Leonbloy,我可以正常连接到两个服务器。 - Ryan
3个回答

11

首先,在调用readLine()之前,您应该调用BufferedReader.ready(),因为ready()会告诉您是否可以进行读取。

PrintWriter不会抛出I/O异常,因此写入可能已经失败而您并不知道,这就是为什么没有任何内容可供阅读的原因。使用PrintWriter.checkError()来查看在写入过程中是否出现了问题。

在向管道中写入任何内容之前,您应该同时设置Socket上的输入和输出流。如果当另一端尝试写入时,您的读取器未准备好,服务器将出现断开的管道,且不再发送任何数据。在您尚未读取或写入任何内容之前,Telnet会设置读取和写入。

您可以利用Wireshark来判断服务器是否正在发送数据。


1
bufferedreader.ready 似乎解决了问题,感谢您提供的 printwriter.checkerror 提示,我会将其添加到我的代码中。 - Ryan
@DavidNewcomb 为什么在发送数据之前,我应该从套接字的InputStream设置一个BufferedReader?它真的会吃掉先到达的数据吗?对于始终可用初始数据的数据源,这是如何工作的? - 我期望任何流包装器都可以获取,但不会吃掉初始数据? - class stacker
1
使用BufferedXxx,您无法确定读取或写入实际发生的时间。通信通道是一个管道,管道必须有读者和写者共同工作。它不会吞噬数据,如果您尝试写入某些内容但没有人读取它,则会出现断开的管道,就像在另一端没有人写入时,您的读取将被阻塞一样。 - David Newcomb
1
BufferedReader.ready()在这里和大多数其他情况下都是无意义的。它不能告诉你一行是否已经完整到达,因此readLine()仍然可能会一直阻塞,直到行终止符到达。 - user207421
@EJP,在这种情况下它起作用了,不是吗!通常,如果您正在读取行,则另一端的内容正在写入行,因此系统会平衡,因为在另一端的回车强制执行流刷新。当您调用ready()时,很可能会获得整个行。从阅读代码BufferedReader.readLine()以非阻塞方式读取任何可用数据,直到流结束或达到新行。虽然readLine似乎会阻塞,但实际上它会内部循环检查数据并读取数据。这是一个微妙的区别,但在谈论CPU使用率时非常重要。 - David Newcomb
@EJP,BufferedReader专门用于“从字符输入流中读取文本”,因此如果您在二进制流上使用它,那么您将得到应有的结果 ;) - David Newcomb

10

BufferedReader.readLine() 读取行,即以\r\r\n结尾的字符序列。我猜测您的服务器将其输出写入了单个行中。您的 telnet 输出证实了这一假设。在服务器端使用 PrintWriter.println() 即可解决。


1
或者他必须使用read(char[] cbuf, int off, int len),如果它是一个长连续的流。 - toto2

-1

这个和我一起工作 使用套接字而不使用flush

void start_listen()
   {
       String    result1="";
       char[] incoming = new char[1024];
       while (!s.isClosed())
       {
           try {

           int lenght  =  input.read(incoming);
               result1 = String.copyValueOf(incoming,0,lenght);


           }
           catch (IOException e)
           {
               e.printStackTrace();
           }
           Log.d("ddddddddddd",result1);

       }

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