![](/img/trans.png)
[英]Java NIO inherent flaw in reading from one socket and writing to another?
[英]Java NIO reading and writing from distant machine
我想使用NIO向/從遠程機器發送/接收數據。 我可以隨時發送或接收數據,當我需要發送數據時,我只是發送它而沒有來自遠程計算機的任何查詢,並且遠程計算機會定期發送給我數據。 我不了解NIO機制。 什么在選擇器SelectionKey上生成和讀取或寫入事件? 是否可以僅在我這一邊使用一個ServerSocketChannel從遠程計算機讀取數據並將數據寫入其中? 這就是我的理解,但我不知道如何觸發書寫事件……謝謝您的解釋。
我已經做過一些編碼,可以讀取來自遠程計算機的數據,但無法寫入。 我使用選擇器,我不知道該如何寫數據。 記錄的消息“ handle write”從未被寫入,但是在wireshark中我可以看到我的數據包。
public class ServerSelector {
private static final Logger logger = Logger.getLogger(ServerSelector.class.getName());
private static final int TIMEOUT = 3000; // Wait timeout (milliseconds)
private static final int MAXTRIES = 3;
private final Selector selector;
public ServerSelector(Controller controller, int... servPorts) throws IOException {
if (servPorts.length <= 0) {
throw new IllegalArgumentException("Parameter(s) : <Port>...");
}
Handler consolehHandler = new ConsoleHandler();
consolehHandler.setLevel(Level.INFO);
logger.addHandler(consolehHandler);
// Create a selector to multiplex listening sockets and connections
selector = Selector.open();
// Create listening socket channel for each port and register selector
for (int servPort : servPorts) {
ServerSocketChannel listnChannel = ServerSocketChannel.open();
listnChannel.socket().bind(new InetSocketAddress(servPort));
listnChannel.configureBlocking(false); // must be nonblocking to register
// Register selector with channel. The returned key is ignored
listnChannel.register(selector, SelectionKey.OP_ACCEPT);
}
// Create a handler that will implement the protocol
IOProtocol protocol = new IOProtocol();
int tries = 0;
// Run forever, processing available I/O operations
while (tries < MAXTRIES) {
// Wait for some channel to be ready (or timeout)
if (selector.select(TIMEOUT) == 0) { // returns # of ready chans
System.out.println(".");
tries += 1;
continue;
}
// Get iterator on set of keys with I/O to process
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while (keyIter.hasNext()) {
SelectionKey key = keyIter.next(); // Key is a bit mask
// Server socket channel has pending connection requests?
if (key.isAcceptable()) {
logger.log(Level.INFO, "handle accept");
protocol.handleAccept(key, controller);
}
// Client socket channel has pending data?
if (key.isReadable()) {
logger.log(Level.INFO, "handle read");
protocol.handleRead(key);
}
// Client socket channel is available for writing and
// key is valid (i.e., channel not closed) ?
if (key.isValid() && key.isWritable()) {
logger.log(Level.INFO, "handle write");
protocol.handleWrite(key);
}
keyIter.remove(); // remove from set of selected keys
tries = 0;
}
}
}
}
協議
public class IOProtocol implements Protocol {
private static final Logger logger = Logger.getLogger(IOProtocol.class.getName());
IOProtocol() {
Handler consolehHandler = new ConsoleHandler();
consolehHandler.setLevel(Level.INFO);
logger.addHandler(consolehHandler);
}
/**
*
* @param key
* @throws IOException
*/
@Override
public void handleAccept(SelectionKey key, Controller controller) throws IOException {
SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept();
clntChan.configureBlocking(false); // Must be nonblocking to register
controller.setCommChannel(clntChan);
// Register the selector with new channel for read and attach byte buffer
SelectionKey socketKey = clntChan.register(key.selector(), SelectionKey.OP_READ | SelectionKey.OP_WRITE, controller);
}
/**
* Client socket channel has pending data
*
* @param key
* @throws IOException
*/
@Override
public void handleRead(SelectionKey key) throws IOException {
Controller ctrller = (Controller)key.attachment();
try {
ctrller.readData();
} catch (CommandUnknownException ex) {
logger.log(Level.SEVERE, null, ex);
}
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
/**
* Channel is available for writing, and key is valid (i.e., client channel
* not closed).
*
* @param key
* @throws IOException
*/
@Override
public void handleWrite(SelectionKey key) throws IOException {
Controller ctrl = (Controller)key.attachment();
ctrl.writePendingData();
if (!buf.hasRemaining()) { // Buffer completely written ?
// Nothing left, so no longer interested in writes
key.interestOps(SelectionKey.OP_READ);
}
buf.compact();
}
}
控制器
/**
* Fill buffer with data.
* @param msg The data to be sent
* @throws IOException
*/
private void writeData(AbstractMsg msg) throws IOException {
//
writeBuffer = ByteBuffer.allocate(msg.getSize() + 4);
writeBuffer.putInt(msg.getSize());
msg.writeHeader(writeBuffer);
msg.writeData(writeBuffer);
logger.log(Level.INFO, "Write data - message size : {0}", new Object[]{msg.getSize()});
logger.log(Level.INFO, "Write data - message : {0}", new Object[]{msg});
}
/**
* Write to the SocketChannel
* @throws IOException
*/
public void writePendingData() throws IOException {
commChannel.write(writeBuffer);
}
ServerSocketChannel
用於建立連接,但不發送數據。 每個連接需要一個ServerSocketChannel
和一個SocketChannel
。
使用SocketChannel
進行讀寫的示例:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
您的程序將在第二行進入睡眠狀態,直到數據到來。 您需要將此代碼置於無限循環中,並在后台Thread
運行它。 當數據到來時,您可以從該線程處理它,然后等待其他數據到來。
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put("Hello!".getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
沒有阻塞方法,因此,如果發送小字節緩沖區,則可以從主Thread
調用此方法。
添加:不要在新連接上設置OP_WRITE
鍵。 僅OP_READ
。 當您要寫入一些數據時,需要通知選擇器您要發送某些內容並在事件循環中發送它。 好的解決方案是將待發郵件放入Queue
。 然后按照以下步驟操作:
Queue
添加數據 OP_WRITE
設置為通道的鍵 while (keyIter.hasNext())
循環中,您將具有writable key
,從隊列寫入所有數據並刪除OP_WRITE
密鑰。 我很難理解您的代碼,但我想您會發現問題所在。 另外,如果只想建立一個連接,則無需使用Selector
。 綁定幾個ServerSocketChannels
很奇怪。
我建議您使用阻塞NIO(這是SocketChannel的默認行為),您無需使用選擇器,但可以使用一個線程進行讀取,而使用另一個線程進行寫入。
根據您的示例。
private final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024*1024);
private void writeData(AbstractMsg msg) {
writeBuffer.clear();
writeBuffer.putInt(0); // set later
msg.writeHeader(writeBuffer);
msg.writeData(writeBuffer);
writeBuffer.putInt(0, writeBuffer.position());
writeBuffer.flip();
while(writeBuffer.hasRemaining())
commChannel.write(writeBuffer);
}
什么在選擇器SelectionKey上生成和讀取或寫入事件?
OP_READ:套接字接收緩沖區中存在數據或EOS。
OP_WRITE:在套接字發送緩沖區中的空間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.