Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel:
打开一个SocketChannel并连接到互联网上的某台服务器。
一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。
SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9999)); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //do something with socketChannel... }
如以下代码,
MyClient.java
package com.lyx.nio.socket; 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; public class MyClient { private final static Logger logger = Logger.getLogger(MyClient.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是一个连接到TCP网络套接字的通道 SocketChannel socketChannel = null; try { //创建一个SocketChannel socketChannel = SocketChannel.open(); //InetSocketAddress此类实现IP套接字地址(IP 地址 + 端口号) 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 //是一个直接字节缓冲区 ByteBuffer buffer = ByteBuffer.allocateDirect(1024); byte[] bytes; int count = 0; while ((count = socketChannel.read(buffer)) >= 0) { /** * flip()方法 * flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。 * 换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等 —— 现在能读取多少个byte、char等。 */ buffer.flip(); bytes = new byte[count]; //使用get()方法从Buffer中读取数据 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; } } }
MyServer.java
package com.lyx.nio.socket; 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; public class MyServer { private final static Logger logger = Logger.getLogger(MyServer.class.getName()); public static void main(String[] args) { Selector selector = null; ServerSocketChannel serverSocketChannel = null; try { //打开一个selector 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; /** * 从SocketChannel中读取数据 * socketChannel.read(buffer) * 首先,分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中。 * 然后,调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。 * read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。 */ 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 * 写数据到SocketChannel用的是SocketChannel.write()方法, * 该方法以一个Buffer作为参数 */ socketChannel.write(buffer); } }
辅助类
MyRequestObject.java
package com.lyx.nio.socket; 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(); } }
MyResponseObject.java
package com.lyx.nio.socket; 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(); } }
SerializableUtil.java
package com.lyx.nio.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) { } } } }
==========END==========