【问题标题】:Socket is closed after reading from it's InputStream套接字在从 InputStream 读取后关闭
【发布时间】:2020-10-07 17:52:32
【问题描述】:

向对端发送消息的方法:

    public static void writeLineToPeer(Peer peer, String message) throws IOException
    {
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(peer.getSocket().getOutputStream(), StandardCharsets.UTF_8));
        
        writer.write(message + "\n");
        writer.flush();
    }

阅读回复:

public class PeersResponse extends Response<Peer>
{
    public PeersResponse(InputStream stream) throws InvalidMessageException, IOException
    {
        super(stream);
        parseResponse();
        getNextElement();
    }
    
    @Override
    protected void parseResponse() throws InvalidMessageException, IOException
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(this.stream, "UTF-8"));
        
        setVersion(reader.readLine());
            
        setAmount(reader.readLine());
        //Fine until here
    }

    @Override
    public Peer getNextElement() throws InvalidMessageException, IOException
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(this.stream, "UTF-8"));
        
        String ip = reader.readLine();
        //Socket exception: Socket is closed

        if (NetworkHelper.isValidIPAddress(ip))
        {
            throw new InvalidMessageException();
        }
            
        String port = reader.readLine();
            
        String typeOfPeer = reader.readLine();
    
        if (typeOfPeer.length() != 1)
        {
            throw new InvalidMessageException();
        }
            
        try
        {
            return PeerFactory.getPeer(typeOfPeer.charAt(0), ip, Integer.parseInt(port));
        }
        catch (Exception ex)
        {
            throw new InvalidMessageException(ex);
        }
    }

}

如您所见,我收到了一个 Socket 异常:尝试从另一个实例中读取时 Socket 已关闭 BufferedReader 具有相同的 InputStream。

正在发送的消息很好,适合这个类的结构。

这是什么原因?我该如何解决这个问题?

【问题讨论】:

  • 我相信this.streamparseResponse() 返回后将被关闭。您不应该在单个流上创建多个阅读器,您应该有一个实例级阅读器或PeersResponse 的方法应该有一个BufferedReader reader 参数。此外,BufferedReader#readLine 可以返回 null,如果没有内容可阅读,您应该检查null,以防出现格式错误的消息(尤其是关于if (typeOfPeer.length() != 1) // &lt;-- NullPointerException)。
  • 我尝试创建一个实例级阅读器并在parseResponse()getNextElement() 上使用它,但我仍然遇到同样的异常。感谢您的提示,我将确保为空检查。
  • 在这种情况下,接收到的消息一定有问题,或者您的代码的不同部分中的套接字发生了问题,此处未显示。您是否检查过setVersion(...)setAmount(...) 的结果,如果它们设置为您所期望的?
  • 您可能应该使用相同的 BufferedReader 而不是重新创建它。你关闭它吗?关闭它会关闭底层的 inputStream。

标签: java sockets java-stream


【解决方案1】:

第一次在parseResponse() 内部调用reader.ReadLine() 时,BufferedReader 从流中读取数据块。它读取的不仅仅是一行,因为它还不知道行尾在哪里。它从套接字中读取(和删除)一行的唯一方法是一个接一个地读取字符并在看到新行时停止。但这首先会破坏使用BufferedReader 的目的。

第二次在parseResponse() 内部调用reader.ReadLine(),它根本不会从底层套接字读取。它只是解析已经存储在BufferedReader 中的数据并返回下一行。

parseResponse() 退出时,BufferedReader 内部仍存储有数据。随着 reader 变量超出范围,该数据将丢失。无法从套接字中读取,因为它已经从套接字中读取了。

TLDR; 您可以在调试器中单步执行parseResponse() 并查看 reader 实例 内部(检查JDK 的内部数据结构)。您将在那里看到您的整个消息。当parseResponse() 返回时,您将看到消息的其余部分是如何丢失的。

【讨论】:

  • 所以基本上整个消息都加载到内存中了?
  • 如果消息足够小并且一次全部发送,则整个消息会加载到内存中。
  • 我在OutputStream中写入了多次,所以它不会将所有内容都加载到内存中,对吧?
  • 它可能会也可能不会阅读整个消息。但在几乎情况下,它会读取的不仅仅是版本和内存量。即使您不一次写入整条消息,它仍可能将整条消息加载到内存中。这里有很多可变性。
  • 通过在 parseResponse() 中设置断点并检查 reader 变量,您将获得很多洞察力。
猜你喜欢
  • 1970-01-01
  • 2015-07-30
  • 2012-02-04
  • 2015-05-24
  • 1970-01-01
  • 2013-04-19
  • 2022-06-16
  • 1970-01-01
  • 2020-11-01
相关资源
最近更新 更多