![](/img/trans.png)
[英]java nio SocketChannel.read does not return -1 to indicate end-of-stream
[英]Read on SocketChannel reaches end-of-stream after ServerSocketChannel performs accept
我在下面粘貼了服務器端代碼段。 該服務器代碼在正常情況下可以工作,但是,以下情況設法破壞了代碼。 服務器和客戶端在同一台計算機上。 我使用了環回地址和實際的IP地址,沒有區別。
腳本
WritableByteChannel.write(ByteBuffer src)
返回12個字節,這是正確的大小,但研究表明,這僅意味着將12個字節寫入TCP緩沖區)。 key.isReadable()
條件處理if子句, 但隨后在讀取失敗,表示流結束。 創建SSCCE太復雜了,如果重要信息丟失或過於抽象,請發表評論,我將提供更多信息。
題
新創建/接受的通道如何在讀取操作中失敗? 我想念什么? 我可以采取什么步驟來防止這種情況?
我已經嘗試過wireshark,但是即使通信正常運行,也無法在指定的TCP端口上捕獲任何數據包。
問題/其他信息
代碼段
片段1
while (online)
{
if (selector.select(5000) == 0)
continue;
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext())
{
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable())
{
log.log(Level.INFO, "Starting ACCEPT!");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
log.log(Level.INFO, "{0} connected to port {1}!",
new Object[] {channel.socket().getInetAddress().getHostAddress(), isa.getPort()});
}
boolean accepted = false;
if (key.isReadable())
{
log.log(Level.INFO, "Starting READ!");
SocketChannel channel = (SocketChannel) key.channel();
bb.clear();
bb.limit(Header.LENGTH);
try
{
NioUtil.read(channel, bb); // server fails here!
}
catch (IOException e)
{
channel.close();
throw e;
}
bb.flip();
片段2
public static ByteBuffer read(ReadableByteChannel channel, ByteBuffer bb) throws IOException
{
while (bb.remaining() > 0)
{
int read = 0;
try
{
read = channel.read(bb);
}
catch (IOException e)
{
log.log(Level.WARNING, "Error during blocking read!", e);
throw e;
}
// this causes the problem... or indicates it
if (read == -1)
{
log.log(Level.WARNING, "Error during blocking read! Reached end of stream!");
throw new ClosedChannelException();
}
}
return bb;
}
片段3
@Override
public boolean isServerOnline()
{
String host = address.getProperty(PropertyKeys.SOCKET_SERVER_HOST);
int port = Integer.parseInt(address.getProperty(PropertyKeys.SOCKET_SERVER_PORT));
boolean _online = true;
try
{
InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(host), port);
SocketChannel _channel = SocketChannel.open();
_channel.connect(addr);
_channel.close();
}
catch (Exception e)
{
_online = false;
}
return _online;
}
解
問題不在於檢查的方法(如果服務可用/服務器處於聯機狀態)。 問題是EJP提到的第二點。
預期服務器會提供特定的輸入,如果不滿足該條件,則該輸入將處於不一致狀態。 我添加了一些后備措施,現在重新連接過程(包括檢查方法)可以正常工作。
顯然,客戶端必須已關閉連接。 這是read()
返回-1的唯一方法。
筆記:
當read()
返回-1時,您將拋出不合適的ClosedChannelException
。 當您已經關閉通道並繼續使用它時,NIO會拋出該異常。 它與流的末尾無關,不應該用於此目的。 如果必須扔東西,則拋出EOFException
。
您也不應該按自己的方式循環。 您只應在read()
返回正數時循環。 目前,在嘗試讀取可能永遠不會到達的數據時,您正在選擇循環不足。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.