您的位置:首页 > 编程语言 > Java开发

基于java底层api实现的nio

2017-04-07 00:00 393 查看
摘要: ServerSocketChannel SocketChannel 笔记

服务器端

package 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.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Server {

private static ByteBuffer recvBuf = ByteBuffer.allocate(1024);

private static ByteBuffer respBuf = ByteBuffer.allocate(1024);

private static Map<SelectionKey,Object> sessionMessage = new ConcurrentHashMap<SelectionKey,Object>();
public static void main(String[] args) throws IOException {
//多路复用器
Selector selector = Selector.open();

//创建server
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(9999));//绑定端口
server.configureBlocking(false);//设置非阻塞

//server上注册一个多路复用器  事件标签OP_ACCEPT接收客户端连接 这样多路复用器将不断轮询server,当有客户端连接过来,就会被轮询出来
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器已经启动");
//启动监听
listener(server, selector);

}

public static void listener(ServerSocketChannel server, Selector selector ) throws IOException{
while (true) {
int eventCount = selector.select();//如果有客户端连接过来,就会被轮询出来
if (eventCount <= 0) {//小于等于0,说明没有客户端接入
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();//获取有事件触发的selectkey
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
//处理事件
try {
process(server,selector,key);
} catch (IOException e) {//客户端异常关闭
close(key);
}
iter.remove();//处理完移除这个事件
}
}
}

public static void process(ServerSocketChannel server,  Selector selector ,SelectionKey key) throws IOException{
SocketChannel client = null;
if (key.isValid() && key.isAcceptable() ) {//事件是否是有效的
client = server.accept();//有客户端接入,把客户端连接通道也注册到多路复用器上

client.configureBlocking(false);//非阻塞的
client.register(selector, SelectionKey.OP_READ);//客户端连接过来,注册读事件
}else if(key.isValid() && key.isReadable()){//可读的,说明注册在多路复用上的客户端有数据发过来了
recvBuf.clear();//清空缓冲区
client = (SocketChannel) key.channel();//通过key获取到客户端,这个客户端是在Acceptable时连接过来的客户端
int length = client.read(recvBuf);//将数据读到缓冲区
if (length > 0) {//读取到内容的时候
String message = new String(recvBuf.array(),0,length);
sessionMessage.put(key, message);//将数据放到sessionmessage中
key.interestOps(SelectionKey.OP_WRITE);//读完数据后,这个客户端在多路复用器上的事件由read变为write,可以写了
}else{
if (client.isConnected()) {
System.out.println("客户端关闭");
close(key);
}
}
}else if(key.isValid() && key.isWritable()){//可写的,说明注册在多路复用上的客户端数据已经接收完了,可以往客户端写数据了
if (!sessionMessage.containsKey(key)) {
return;
}
//获取到客户端发送过来的数据
client = (SocketChannel) key.channel();
Object resp = handler(sessionMessage.get(key));//将读取到的数据暴露给用户处理,得到响应数据
respBuf.clear();
respBuf.put((ByteBuffer)resp);
respBuf.flip();
client.write(respBuf);//响应客户端
key.interestOps(SelectionKey.OP_READ);//写完数据后,这个客户端在多路复用器上的事件又由write变为read,可以读了
}else{
client = server.accept();//有客户端接入,把客户端连接通道也注册到多路复用器上
System.out.println("连接状态::"+client.isConnected());
}
}

public static ByteBuffer handler(Object message){
System.out.println("来自客户端的消息:"+message);
return ByteBuffer.wrap((message+"这是响应").getBytes());

}

public static void close(SelectionKey key){
key.cancel();//取消注册
SocketChannel client = (SocketChannel) key.channel();
try {
if (client != null) {
client.socket().close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端

package 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.SocketChannel;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Client {
private static ByteBuffer sendBuf = ByteBuffer.allocate(1024);

private static ByteBuffer respBuf = ByteBuffer.allocate(1024);

private static Map<SelectionKey,Object> sessionMessage = new ConcurrentHashMap<SelectionKey,Object>();
public static void main(String[] args) throws IOException {
//多路复用器
Selector selector = Selector.open();

//客户端
SocketChannel client = SocketChannel.open();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_CONNECT);//注册连接事件
client.connect(new InetSocketAddress("localhost", 9999));//连接服务器,触发connect事件
//监听客户端事件
listener(client,selector);
}

private static void listener(SocketChannel client, Selector selector) throws IOException {
while (true) {
int eventCount = selector.select();//如果有客户端连接过来,就会被轮询出来
if (eventCount <= 0) {//小于等于0,说明没有事件触发
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();//获取有事件触发的selectkey
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
//处理事件
try {
process(client,selector,key);
} catch (IOException e) {//客户端异常关闭
//close(key);
}
iter.remove();//处理完移除这个事件
}

}
}

private static void process(SocketChannel client, Selector selector,
SelectionKey key) throws IOException{
Scanner scan = new Scanner(System.in);
SocketChannel channel = null;
if (key.isValid() && key.isConnectable() ) {//事件是否是有效的
channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {//如果正在连接,完成连接
channel.configureBlocking(false);
channel.finishConnect();
System.out.println("客户端连接成功");
}
client.register(selector, SelectionKey.OP_WRITE);//客户端连接上了,可以写了
}else if(key.isValid() && key.isWritable()){//可写了,向服务器端发送消息
System.out.println("可以写了-----");
String line = scan.next();
if (line.equals("finsh")) {
System.out.println("关闭了...");
key.cancel();
client.close();
System.exit(1);
}else{
sendBuf.clear();
sendBuf.put(line.getBytes());
sendBuf.flip();
client.write(sendBuf);//响应客户端
key.interestOps(SelectionKey.OP_READ);//写完数据后,这个客户端在多路复用器上的事件又由write变为read,可以读了
}
}else if(key.isValid() && key.isReadable()){//可读,获取服务器端响应
System.out.println("可以读了-----");
respBuf.clear();//清空缓冲区
int length = client.read(respBuf);//将数据读到缓冲区
if (length > 0) {//读取到内容的时候
String message = new String(respBuf.array(),0,length);
System.out.println("client-响应信息::::"+message);
key.interestOps(SelectionKey.OP_WRITE);//读完数据后,这个客户端在多路复用器上的事件由read变为write,可以写了
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: