我粘貼了下面的服務器端代碼片段。此服務器代碼在正常情況下工作,但是,下面的方案管理破壞代碼。 服務器和客戶端位於同一臺計算機上。我使用了回送地址和實際的IP地址,這沒什麼區別。對SocketChannel的讀取在ServerSocketChannel執行接受後達到數據流尾端
方案
- 服務器在線,客戶端發出請求(
WritableByteChannel.write(ByteBuffer src)
返回12字節,這是正確的大小,但研究顯示,只有意味着12個字節寫入TCP緩衝區)。 - 服務器程序已關閉。客戶注意到通道在遠端被關閉並且在它自己的一端關閉它,它沒有提出任何請求。
- 服務器再次聯機。
- 客戶端嘗試發出請求,但失敗,因爲通道已關閉/無效並且無法重用(即使服務器再次聯機)。
- 客戶端檢查服務器的在線狀態,得到肯定的結果,再次連接並立即發出另一個請求。
- 服務器接受客戶端(代碼如下),然後處理帶有
key.isReadable()
條件的if子句,,但是在讀取時失敗,這表示數據流結束。
創建SSCCE會太複雜,請留下評論重要信息是否丟失或太抽象,我會提供更多信息。
問題
如何能在新創建/接受通道上的讀取操作失敗? 我錯過了什麼?我可以採取哪些措施來防止這種情況?
我已經試過了wireshark,但是我無法捕獲指定TCP端口上的任何數據包,即使通信是非常有效的。
問題/附加信息
- 有可能捕獲數據包到.pcap文件與RawCap
- 的問題是客戶端檢查服務器狀態的方式。我已經添加了下面的方法。
代碼段
片段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提到的第二點。
服務器希望獲得特定輸入,如果未滿足條件,則該輸入處於不一致狀態。 我已經添加了一些後備措施,現在重新連接過程 - 包括檢查方法 - 工作正常。
因此,這意味着我應該thouroghly修改重新連接過程。下次我在工作時,我會檢查一些隱藏的close()(...將在下週,因爲我是一名學生)並接受你的答案。 – mike 2015-02-11 16:00:01
Thx也適合您的第二點。我知道你的意思,但這只是一個小小的握手的一部分(帶有兩個小的協議單元,這些單元一起發送),但主要通信使用非阻塞閱讀 – mike 2015-02-11 16:03:40
@mike無關緊要。這是錯誤的,需要修復。否則,當它破壞別人可能會更糟。案例點[這裏](http://stackoverflow.com/a/28421784/207421)。 – EJP 2015-02-11 16:19:20