服务端时序图
HandleServer.java类
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.Date;
import java.util.Iterator;
import java.util.Set;
public class HandleServer implements Runnable{
public Selector selector;
public ServerSocketChannel servChanner;
public volatile boolean stop;
public HandleServer(int port) {
try {
//打开ServerSocketChannel,用于监听客户端的连接,是所有客户端的父连接
servChanner=ServerSocketChannel.open();
//设置为非阻塞
servChanner.configureBlocking(false);
//绑定监听端口
servChanner.socket().bind(new InetSocketAddress(port), 1024);
//创建打开多路复用器
selector=Selector.open();
//将客户端连接注册到多路复用器,并监听accept事件
servChanner.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("The time server is start in port:"+port);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void stop(){
this.stop=true;
}
public void run(){
//多路复用器在线程run方法的无限循环体内轮询准备就绪的Key
while(!stop){
try {
//复用器等待1000毫秒
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it=selectedKeys.iterator();
SelectionKey key=null;
while(it.hasNext()){
key=it.next();
it.remove();
//如果有事件发生,则处理
handleinput(key);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(selector!=null){
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handleinput(SelectionKey key)throws IOException{
//检查key是否有效
if(key.isValid()){
//判断这个key对应的channel是否已经准备好接收连接
if(key.isAcceptable()){
ServerSocketChannel ssc=(ServerSocketChannel)key.channel();
//处理接收请求,完成tcp三次握手
SocketChannel sc=ssc.accept();
//将新接收的客户端连接注册到Reactor线程的多路复用器上,监听读操作
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
//判断可读事件
if(key.isReadable()){
//获取读操作的通道
SocketChannel sc=(SocketChannel)key.channel();
//建立buffer缓存
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
//通道开始往缓存读数据
//n 有数据的时候返回读取到的字节数。
//0 没有数据并且没有达到流的末端时返回0。
//-1 当达到流末端的时候返回-1。
int readBytes=sc.read(readBuffer);
if(readBytes>0){
readBuffer.flip();
byte[] bytes=new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body=new String(bytes,"utf-8");
System.out.println("this time server receive order:"+body);
String currentTime="query time order".equalsIgnoreCase(body)
?new Date(System.currentTimeMillis()).toString():"bad order";
doWrite(sc,currentTime);
}else if(readBytes<0){
//小于零证明读写完了,关闭通道
key.cancel();
sc.close();
}else{
}
}
}
}
private void doWrite(SocketChannel channer,String response) throws IOException{
if(response!=null && response.trim().length()>0){
byte[] bytes=response.getBytes();
ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
//注意此处的flip方法,是将当前位置设置为EOF,指针指向0,这样为客户端的读模式做好准备
writeBuffer.flip();
channer.write(writeBuffer);
}
}
}
以上是服务端代码,客户端代码如下:
TimeClient.java类
public class TimeClient {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new TimeClientHandle("127.0.0.1",8080),"client-001").start();
}
}
TimeClientHandle.java类
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.Set;
public class TimeClientHandle implements Runnable{
private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
public TimeClientHandle(String host,int port){
//设置ip和端口
this.host=host== null?"127.0.0.1":host;
this.port=port;
try {
//打开,绑定,这里不详说
selector=Selector.open();
socketChannel=SocketChannel.open();
socketChannel.configureBlocking(false);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run(){
try {
doConnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//轮询
while(!stop){
try {
selector.select(1000);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Set<SelectionKey> selectedKeys=selector.selectedKeys();
Iterator<SelectionKey> it=selectedKeys.iterator();
SelectionKey key=null;
while(it.hasNext()){
key=it.next();
it.remove();
try {
//处理key
handleinput(key);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
if(selector!=null){
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handleinput(SelectionKey key)throws IOException{
//查看key是否有效
if(key.isValid()){
//通过key获取有效的通道
SocketChannel sc=(SocketChannel)key.channel();
//判断是否已建立连接
if(key.isConnectable()){
//如果已经完成连接
if(sc.finishConnect()){
//注册读事件
sc.register(selector, SelectionKey.OP_READ);
//写入
dowrite(sc);
}else{
System.exit(1);
}
//当为可读事件时
if(key.isReadable()){
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
int readBytes=sc.read(readBuffer);
if(readBytes>0){
readBuffer.flip();
byte[] bytes=new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body=new String(bytes,"utf-8");
System.out.println("now is:"+body);
this.stop=stop;
}else if(readBytes<0){
key.cancel();
sc.close();
}else{}
}
}
}
}
private void doConnect() throws IOException{
//ip,端口,异步连接服务器,判断是否成功
if(socketChannel.connect(new InetSocketAddress(host, port))){
//如果成功,直接注册读事件。
socketChannel.register(selector, SelectionKey.OP_READ);
//往服务端写数据
dowrite(socketChannel);
}else{
//如果没有成功(即异步连接返回false,说明客户端已发送sync同步包,服务器没返回ack包,物理连接还没建立)
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
private void dowrite(SocketChannel sc)throws IOException{
byte[] req="query time orderkkk".getBytes();
ByteBuffer writeBuffer=ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
sc.write(writeBuffer);
if(!writeBuffer.hasRemaining()){
System.out.println("send order 2 server succeed");
}
}
}