Java使用NIO包實(shí)現(xiàn)Socket通信的實(shí)例代碼
前面幾篇文章介紹了使用java.io和java.net類庫實(shí)現(xiàn)的Socket通信,下面介紹一下使用java.nio類庫實(shí)現(xiàn)的Socket。
java.nio包是Java在1.4之后增加的,用來提高I/O操作的效率。在nio包中主要包括以下幾個(gè)類或接口:
- Buffer:緩沖區(qū),用來臨時(shí)存放輸入或輸出數(shù)據(jù)。
- Charset:用來把Unicode字符編碼和其它字符編碼互轉(zhuǎn)。
- Channel:數(shù)據(jù)傳輸通道,用來把Buffer中的數(shù)據(jù)寫入到數(shù)據(jù)源,或者把數(shù)據(jù)源中的數(shù)據(jù)讀入到Buffer。
- Selector:用來支持異步I/O操作,也叫非阻塞I/O操作。
nio包中主要通過下面兩個(gè)方面來提高I/O操作效率:
- 通過Buffer和Channel來提高I/O操作的速度。
- 通過Selector來支持非阻塞I/O操作。
下面來看一下程序中是怎么通過這些類庫實(shí)現(xiàn)Socket功能。
首先介紹一下幾個(gè)輔助類
輔助類SerializableUtil,這個(gè)類用來把java對象序列化成字節(jié)數(shù)組,或者把字節(jié)數(shù)組反序列化成java對象。
package com.googlecode.garbagecan.test.socket; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializableUtil { public static byte[] toBytes(Object object) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(baos); oos.writeObject(object); byte[] bytes = baos.toByteArray(); return bytes; } catch(IOException ex) { throw new RuntimeException(ex.getMessage(), ex); } finally { try { oos.close(); } catch (Exception e) {} } } public static Object toObject(byte[] bytes) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = null; try { ois = new ObjectInputStream(bais); Object object = ois.readObject(); return object; } catch(IOException ex) { throw new RuntimeException(ex.getMessage(), ex); } catch(ClassNotFoundException ex) { throw new RuntimeException(ex.getMessage(), ex); } finally { try { ois.close(); } catch (Exception e) {} } } }
輔助類MyRequestObject和MyResponseObject,這兩個(gè)類是普通的java對象,實(shí)現(xiàn)了Serializable接口。MyRequestObject類是Client發(fā)出的請求,MyResponseObject是Server端作出的響應(yīng)。
package com.googlecode.garbagecan.test.socket.nio; import java.io.Serializable; public class MyRequestObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private String value; private byte[] bytes; public MyRequestObject(String name, String value) { this.name = name; this.value = value; this.bytes = new byte[1024]; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Request [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); return sb.toString(); } } package com.googlecode.garbagecan.test.socket.nio; import java.io.Serializable; public class MyResponseObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private String value; private byte[] bytes; public MyResponseObject(String name, String value) { this.name = name; this.value = value; this.bytes = new byte[1024]; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Response [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); return sb.toString(); } }
下面主要看一下Server端的代碼,其中有一些英文注釋對理解代碼很有幫助,注釋主要是來源jdk的文檔和例子,這里就沒有再翻譯
package com.googlecode.garbagecan.test.socket.nio; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; 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.logging.Level; import java.util.logging.Logger; import com.googlecode.garbagecan.test.socket.SerializableUtil; public class MyServer3 { private final static Logger logger = Logger.getLogger(MyServer3.class.getName()); public static void main(String[] args) { Selector selector = null; ServerSocketChannel serverSocketChannel = null; try { // Selector for incoming time requests selector = Selector.open(); // Create a new server socket and set to non blocking mode serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); // Bind the server socket to the local host and port serverSocketChannel.socket().setReuseAddress(true); serverSocketChannel.socket().bind(new InetSocketAddress(10000)); // Register accepts on the server socket with the selector. This // step tells the selector that the socket wants to be put on the // ready list when accept operations occur, so allowing multiplexed // non-blocking I/O to take place. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // Here's where everything happens. The select method will // return when any operations registered above have occurred, the // thread has been interrupted, etc. while (selector.select() > 0) { // Someone is ready for I/O, get the ready keys Iterator<SelectionKey> it = selector.selectedKeys().iterator(); // Walk through the ready keys collection and process date requests. while (it.hasNext()) { SelectionKey readyKey = it.next(); it.remove(); // The key indexes into the selector so you // can retrieve the socket that's ready for I/O execute((ServerSocketChannel) readyKey.channel()); } } } catch (ClosedChannelException ex) { logger.log(Level.SEVERE, null, ex); } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); } finally { try { selector.close(); } catch(Exception ex) {} try { serverSocketChannel.close(); } catch(Exception ex) {} } } private static void execute(ServerSocketChannel serverSocketChannel) throws IOException { SocketChannel socketChannel = null; try { socketChannel = serverSocketChannel.accept(); MyRequestObject myRequestObject = receiveData(socketChannel); logger.log(Level.INFO, myRequestObject.toString()); MyResponseObject myResponseObject = new MyResponseObject( "response for " + myRequestObject.getName(), "response for " + myRequestObject.getValue()); sendData(socketChannel, myResponseObject); logger.log(Level.INFO, myResponseObject.toString()); } finally { try { socketChannel.close(); } catch(Exception ex) {} } } private static MyRequestObject receiveData(SocketChannel socketChannel) throws IOException { MyRequestObject myRequestObject = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteBuffer buffer = ByteBuffer.allocate(1024); try { byte[] bytes; int size = 0; while ((size = socketChannel.read(buffer)) >= 0) { buffer.flip(); bytes = new byte[size]; buffer.get(bytes); baos.write(bytes); buffer.clear(); } bytes = baos.toByteArray(); Object obj = SerializableUtil.toObject(bytes); myRequestObject = (MyRequestObject)obj; } finally { try { baos.close(); } catch(Exception ex) {} } return myRequestObject; } private static void sendData(SocketChannel socketChannel, MyResponseObject myResponseObject) throws IOException { byte[] bytes = SerializableUtil.toBytes(myResponseObject); ByteBuffer buffer = ByteBuffer.wrap(bytes); socketChannel.write(buffer); } }
下面是Client的代碼,代碼比較簡單就是啟動(dòng)了100個(gè)線程來訪問Server
package com.googlecode.garbagecan.test.socket.nio; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.logging.Level; import java.util.logging.Logger; import com.googlecode.garbagecan.test.socket.SerializableUtil; public class MyClient3 { private final static Logger logger = Logger.getLogger(MyClient3.class.getName()); public static void main(String[] args) throws Exception { for (int i = 0; i < 100; i++) { final int idx = i; new Thread(new MyRunnable(idx)).start(); } } private static final class MyRunnable implements Runnable { private final int idx; private MyRunnable(int idx) { this.idx = idx; } public void run() { SocketChannel socketChannel = null; try { socketChannel = SocketChannel.open(); SocketAddress socketAddress = new InetSocketAddress("localhost", 10000); socketChannel.connect(socketAddress); MyRequestObject myRequestObject = new MyRequestObject("request_" + idx, "request_" + idx); logger.log(Level.INFO, myRequestObject.toString()); sendData(socketChannel, myRequestObject); MyResponseObject myResponseObject = receiveData(socketChannel); logger.log(Level.INFO, myResponseObject.toString()); } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); } finally { try { socketChannel.close(); } catch(Exception ex) {} } } private void sendData(SocketChannel socketChannel, MyRequestObject myRequestObject) throws IOException { byte[] bytes = SerializableUtil.toBytes(myRequestObject); ByteBuffer buffer = ByteBuffer.wrap(bytes); socketChannel.write(buffer); socketChannel.socket().shutdownOutput(); } private MyResponseObject receiveData(SocketChannel socketChannel) throws IOException { MyResponseObject myResponseObject = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ByteBuffer buffer = ByteBuffer.allocateDirect(1024); byte[] bytes; int count = 0; while ((count = socketChannel.read(buffer)) >= 0) { buffer.flip(); bytes = new byte[count]; buffer.get(bytes); baos.write(bytes); buffer.clear(); } bytes = baos.toByteArray(); Object obj = SerializableUtil.toObject(bytes); myResponseObject = (MyResponseObject) obj; socketChannel.socket().shutdownInput(); } finally { try { baos.close(); } catch(Exception ex) {} } return myResponseObject; } } }
最后測試上面的代碼,首先運(yùn)行Server類,然后運(yùn)行Client類,就可以分別在Server端和Client端控制臺(tái)看到發(fā)送或接收到的MyRequestObject或MyResponseObject對象了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java Socket編程實(shí)例(四)- NIO TCP實(shí)踐
- Java NIO實(shí)例UDP發(fā)送接收數(shù)據(jù)代碼分享
- Java Socket編程實(shí)例(五)- NIO UDP實(shí)踐
- 詳解Java 網(wǎng)絡(luò)IO編程總結(jié)(BIO、NIO、AIO均含完整實(shí)例代碼)
- Java NIO Path接口和Files類配合操作文件的實(shí)例
- Java NIO和IO的區(qū)別
- Java 高并發(fā)八:NIO和AIO詳解
- Java NIO工作原理的全面分析
- 基于java編寫局域網(wǎng)多人聊天室
- Java基于中介者模式實(shí)現(xiàn)多人聊天室功能示例
- java使用MulticastSocket實(shí)現(xiàn)基于廣播的多人聊天室
- Java NIO Selector用法詳解【含多人聊天室實(shí)例】
相關(guān)文章
Java使用字節(jié)流實(shí)現(xiàn)圖片音頻的復(fù)制
今天帶大家學(xué)習(xí)Java的相關(guān)知識(shí),文章圍繞著Java如何使用字節(jié)流實(shí)現(xiàn)圖片音頻的復(fù)制展開,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06SpringBoot 創(chuàng)建容器的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot 創(chuàng)建容器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10mybatis學(xué)習(xí)筆記之mybatis注解配置詳解
本篇文章主要介紹了mybatis學(xué)習(xí)筆記之mybatis注解配置詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12SpringBoot操作mongo實(shí)現(xiàn)方法解析
這篇文章主要介紹了SpringBoot操作mongo實(shí)現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java String類詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java String類詳解,本文經(jīng)多方資料的收集整理和歸納,最終撰寫成文,非常不錯(cuò),值得收藏,需要的的朋友參考下2017-04-04Java SimpleDateFormat線程安全問題原理詳解
這篇文章主要介紹了Java SimpleDateFormat線程安全問題原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05