我们用NIO
来实现一个简单的网络通讯程序,服务一直向客户端发送 hello,client。然后客户端一直向服务端发送hello,server。
服务端代码:
package cn.szyrm.network.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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;
public class ServerDemo {
public static void main(String[] args) throws IOException {
Selector selector = getSelector();
listen(selector);
}
/**
* 获取一个selector,并将serverSocket
* @return
* @throws IOException
*/
public static Selector getSelector() throws IOException {
//创建一个selector对象
Selector sel = Selector.open();
// 创建severSocketChannel,并设置为非阻塞的模式
ServerSocketChannel server = sel.provider().openServerSocketChannel();
server.configureBlocking(false);
//绑定通道到指定的端口
ServerSocket socket = server.socket();
socket.bind(new InetSocketAddress(8080));
//向selector注册感兴趣的事件
server.register(sel, SelectionKey.OP_ACCEPT);
return sel;
}
/**
* 服务端进行监听动作
*/
public static void listen(Selector selector){
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
while(true){
//这是一个阻塞的动作
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
//将当前的key 删除掉,防止重复处理
iterator.remove();
processKey(key,selector, buffer);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 处理key
* @param key
*/
private static void processKey(SelectionKey key, Selector selector, ByteBuffer buffer) throws IOException {
if(key.isAcceptable()){
ServerSocketChannel serverSocket = (ServerSocketChannel) key.channel();
SocketChannel accept = serverSocket.accept();
accept.configureBlocking(false);
accept.register(selector,SelectionKey.OP_READ,SelectionKey.OP_WRITE);
}else if(key.isReadable()){ //读事件
SocketChannel channel = (SocketChannel) key.channel();
int len = channel.read(buffer);
if(len > 0 ){
buffer.flip();
String content = new String(buffer.array());
System.out.println( content);
SelectionKey register = channel.register(selector, SelectionKey.OP_WRITE );
register.attach("hello,client");
}else{
channel.close();
}
}else if(key.isWritable()){ //可写事件
SocketChannel channel = (SocketChannel) key.channel();
String attach = (String) key.attachment();
ByteBuffer wrap = ByteBuffer.wrap(( attach).getBytes());
//
channel.write(wrap);
SelectionKey register = channel.register(selector, SelectionKey.OP_READ );
}
}
}
客户端代码:
package cn.szyrm.network.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class ClientDemo {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
SocketAddress socketAddress = new InetSocketAddress("localhost", 8888);
sc.configureBlocking(false);
SocketAddress adr = new InetSocketAddress("localhost",8080);
boolean connect = sc.connect(adr);
Selector s = Selector.open();
sc.register(s, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
while(true){
int select = s.select();
Set<SelectionKey> selectionKeys = s.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey next = iterator.next();
iterator.remove();
processKey(s,next);
}
}
}
private static void processKey( Selector s,SelectionKey key) throws IOException {
SocketChannel sc;
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
if (key.isConnectable()) {//当成功连接到服务端时候触发
sc = (SocketChannel) key.channel();
while ( !sc.finishConnect()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
buffer.put("hello,server".getBytes());
buffer.flip();
sc.write(buffer);
buffer.clear();
sc.register(s, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
} else if (key.isReadable()) {//可读事件就绪时
sc = (SocketChannel) key.channel();
sc.read(buffer);
buffer.flip();
byte[] read = read(buffer);
System.out.println( new String(read));
buffer.clear();
SelectionKey register = sc.register(s, SelectionKey.OP_WRITE );
register.attach("hello,server");
}else if(key.isWritable()){//写事件准备就绪的时候
sc = (SocketChannel) key.channel();
Object attachment = key.attachment();
if(attachment != null ){
buffer.put(attachment.toString().getBytes());
buffer.flip();
sc.write(buffer);
}
buffer.clear();
sc.register(s, SelectionKey.OP_READ );
}
}
/**
* 从buffer将数据读取到数组中
* @param buffer
* @return
*/
public static byte[] read(ByteBuffer buffer){
byte[] bytes = new byte[buffer.limit() - buffer.position()];
buffer.get(bytes);
return bytes;
}
}
如果对用到的Selector,SelectionKey
,ByteBuffer
,Channel
不了解的可以查看
[NIO三大组件之 selector][(https://blog.csdn.net/zhangshenglu1/article/details/105501004)] 、[数据通讯通道(Channel) 之FileChannel用法简单示例][] 、 NIO数据容器buffer简单示例