1、server端
public class SelectorServerDemo {
public static void main(String[] args) throws IOException {
//1、创建selector,管理多个channel
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2、建立selector和channel的联系(注册)
//SelectionKey就是将来事件发生后,通过它可以知道事件和哪个channel的事件
SelectionKey sscKey = ssc.register(selector, 0, null);
//key只关注accept事件
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while (true) {
//3. select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行
selector.select();
//4. 处理事件,selectedKeys内部包含了所有发生的事件
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
//删除SelectionKey(必须删除)
iter.remove();
//5. 区分事件类型
if (key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
//将一个ByteBuffer作为附件关联到selectionKey上,每个channel独有
ByteBuffer buffer = ByteBuffer.allocate(16);
SelectionKey scKey = sc.register(selector, 0, buffer);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("scKey:"+scKey);
} else if(key.isReadable()) {
try {
SocketChannel channel = (SocketChannel) key.channel();
//获取关联的ByteBuffer附件
ByteBuffer buffer = (ByteBuffer)key.attachment();
//正常调用SocketChannel#close方法会返回-1
int read = channel.read(buffer);
if (read == -1) {
//取消注册
key.channel();
} else {
// buffer.flip();
// System.out.println(buffer.toString());
split(buffer);
//判断是否需要扩容
if (buffer.position() == buffer.limit()) {
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);
//旧buffer切换读模式
buffer.flip();
newBuffer.put(buffer);
//重新关联附件
key.attach(newBuffer);
}
}
} catch (Exception e) {
e.printStackTrace();
//强制停止(注意不是调用SocketChannel#close方法)客户端时会进入此异常,并抛出异常:远程主机强迫关闭了一个现有的连接。
//客户端报错时,必须取消注册事件,如果不取消,会一直循环报错,因为事件没有被处理
key.channel();
}
}
}
}
}
/**
* 消息的粘包拆包解决方案
* @param source
*/
public static void split(ByteBuffer source) {
source.flip();
for (int i=0; i < source.limit();i++) {
//找到一条消息
if (source.get(i) == '\n') {
int length = i+1-source.position();
//把这条完整消息存入新的ByteBuffer
ByteBuffer target = ByteBuffer.allocate(length);
//从source读,向target写
for (int j = 0; j < length; j++) {
target.put(source.get());
}
System.out.println(new String(target.array()));
}
}
source.compact();
}
}
2、client端
public class SelectorClientDemo {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost",8080));
SocketAddress address = sc.getLocalAddress();
sc.write(Charset.defaultCharset().encode("hello\nworld\nhhhhhhhhhhhhhh\n"));
System.in.read();
}
}