基于Java NIO的Socket通信
基于Java NIO的Socket通信
Java NIO模式的Socket通信,是一種同步非阻塞IO設(shè)計(jì)模式,它為Reactor模式實(shí)現(xiàn)提供了基礎(chǔ)。
下面看看,Java實(shí)現(xiàn)的一個(gè)服務(wù)端和客戶端通信的例子。
NIO模式的基本原理描述如下:
服務(wù)端打開(kāi)一個(gè)通道(ServerSocketChannel),并向通道中注冊(cè)一個(gè)選擇器(Selector),這個(gè)選擇器是與一些感興趣的操作的標(biāo)識(shí)(SelectionKey,即通過(guò)這個(gè)標(biāo)識(shí)可以定位到具體的操作,從而進(jìn)行響應(yīng)的處理)相關(guān)聯(lián)的,然后基于選擇器(Selector)輪詢(xún)通道(ServerSocketChannel)上注冊(cè)的事件,并進(jìn)行相應(yīng)的處理。
客戶端在請(qǐng)求與服務(wù)端通信時(shí),也可以向服務(wù)器端一樣注冊(cè)(比服務(wù)端少了一個(gè)SelectionKey.OP_ACCEPT操作集合),并通過(guò)輪詢(xún)來(lái)處理指定的事件,而不必阻塞。
下面的例子,主要以服務(wù)端為例,而客戶端只是簡(jiǎn)單地發(fā)送請(qǐng)求數(shù)據(jù)和讀響應(yīng)數(shù)據(jù)。
服務(wù)端實(shí)現(xiàn),代碼如下所示:
package org.shirdrn.java.communications.nio;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; import java.util.logging.Logger;/*** NIO服務(wù)端* * @author shirdrn*/ public class NioTcpServer extends Thread {private static final Logger log = Logger.getLogger(NioTcpServer.class.getName());private InetSocketAddress inetSocketAddress;private Handler handler = new ServerHandler();public NioTcpServer(String hostname, int port) {inetSocketAddress = new InetSocketAddress(hostname, port);}@Overridepublic void run() {try {Selector selector = Selector.open(); // 打開(kāi)選擇器ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打開(kāi)通道serverSocketChannel.configureBlocking(false); // 非阻塞serverSocketChannel.socket().bind(inetSocketAddress);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 向通道注冊(cè)選擇器和對(duì)應(yīng)事件標(biāo)識(shí)log.info("Server: socket server started.");while(true) { // 輪詢(xún)int nKeys = selector.select();if(nKeys>0) {Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();while(it.hasNext()) {SelectionKey key = it.next();if(key.isAcceptable()) {log.info("Server: SelectionKey is acceptable.");handler.handleAccept(key);} else if(key.isReadable()) {log.info("Server: SelectionKey is readable.");handler.handleRead(key);} else if(key.isWritable()) {log.info("Server: SelectionKey is writable.");handler.handleWrite(key);}it.remove();}}}} catch (IOException e) {e.printStackTrace();}}/*** 簡(jiǎn)單處理器接口* * @author shirdrn*/interface Handler {/*** 處理{@link SelectionKey#OP_ACCEPT}事件* @param key * @throws IOException*/void handleAccept(SelectionKey key) throws IOException;/*** 處理{@link SelectionKey#OP_READ}事件* @param key * @throws IOException*/void handleRead(SelectionKey key) throws IOException;/*** 處理{@link SelectionKey#OP_WRITE}事件* @param key * @throws IOException*/void handleWrite(SelectionKey key) throws IOException;}/*** 服務(wù)端事件處理實(shí)現(xiàn)類(lèi)* * @author shirdrn*/class ServerHandler implements Handler {@Overridepublic void handleAccept(SelectionKey key) throws IOException {ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();SocketChannel socketChannel = serverSocketChannel.accept();log.info("Server: accept client socket " + socketChannel);socketChannel.configureBlocking(false);socketChannel.register(key.selector(), SelectionKey.OP_READ);}@Overridepublic void handleRead(SelectionKey key) throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocate(512);SocketChannel socketChannel = (SocketChannel)key.channel();while(true) {int readBytes = socketChannel.read(byteBuffer);if(readBytes>0) {log.info("Server: readBytes = " + readBytes);log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes));byteBuffer.flip();socketChannel.write(byteBuffer);break;}}socketChannel.close();}@Overridepublic void handleWrite(SelectionKey key) throws IOException {ByteBuffer byteBuffer = (ByteBuffer) key.attachment();byteBuffer.flip();SocketChannel socketChannel = (SocketChannel)key.channel();socketChannel.write(byteBuffer);if(byteBuffer.hasRemaining()) {key.interestOps(SelectionKey.OP_READ);}byteBuffer.compact();}}public static void main(String[] args) {NioTcpServer server = new NioTcpServer("localhost", 1000);server.start();} }客戶端實(shí)現(xiàn),代碼如下所示:
package org.shirdrn.java.communications.nio;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.logging.Logger;/*** NIO客戶端* * @author shirdrn*/ public class NioTcpClient {private static final Logger log = Logger.getLogger(NioTcpClient.class.getName());private InetSocketAddress inetSocketAddress;public NioTcpClient(String hostname, int port) {inetSocketAddress = new InetSocketAddress(hostname, port);}/*** 發(fā)送請(qǐng)求數(shù)據(jù)* @param requestData*/public void send(String requestData) {try {SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);socketChannel.configureBlocking(false);ByteBuffer byteBuffer = ByteBuffer.allocate(512);socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));while (true) {byteBuffer.clear();int readBytes = socketChannel.read(byteBuffer);if (readBytes > 0) {byteBuffer.flip();log.info("Client: readBytes = " + readBytes);log.info("Client: data = " + new String(byteBuffer.array(), 0, readBytes));socketChannel.close();break;}}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {String hostname = "localhost";String requestData = "Actions speak louder than words!";int port = 1000;new NioTcpClient(hostname, port).send(requestData);} }上述實(shí)現(xiàn),NioTcpServer服務(wù)線程啟動(dòng)后,監(jiān)聽(tīng)指定端口,等待客戶端請(qǐng)求的到來(lái),然后NioTcpClient客戶端進(jìn)程啟動(dòng)并發(fā)送請(qǐng)求數(shù)據(jù),服務(wù)端接收到請(qǐng)求數(shù)據(jù)后,響應(yīng)客戶端(將請(qǐng)求的數(shù)據(jù)作為響應(yīng)數(shù)據(jù)寫(xiě)回到客戶端通道SocketChannel,并等待客戶端處理)。
實(shí)際上,客戶端和服務(wù)端可以采用同樣輪詢(xún)的非阻塞模式來(lái)實(shí)現(xiàn),為簡(jiǎn)單實(shí)現(xiàn)在這個(gè)例子中我們把客戶端角色簡(jiǎn)化了,而實(shí)際上它可能在另一個(gè)系統(tǒng)通信中充當(dāng)服務(wù)端角色。
另外,上面對(duì)于不同事件是采用非線程的方式來(lái)處理,只是簡(jiǎn)單地調(diào)用處理的方法。在實(shí)際中,如果存在大量連接、讀寫(xiě)請(qǐng)求,可以考慮使用線程池來(lái)更大程度地并發(fā)處理,提高服務(wù)端處理的速度和吞吐量,提升系統(tǒng)性能。
總結(jié)
以上是生活随笔為你收集整理的基于Java NIO的Socket通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java NIO和Reactor模式
- 下一篇: Java的IO:BIO | NIO |