服务端代码
package com.li.springboot.nio.server;
import java.io.ByteArrayOutputStream;
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;
/**
* nio通信服务端
* @author 张宗海
*
*/
public class NioServer {
/**
* 接收
*/
public void accept(){
try {
//记录套接字通道事件
Selector selector = Selector.open();
//定义一个异步socket对象
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false); //设置异步
ServerSocket socket = ssc.socket(); //获取socket对象
//绑定端口
InetSocketAddress address = new InetSocketAddress(8083);
socket.bind(address);
//将事件注册selector对象内
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("端口注册完毕!");
while(true){
//查询事件如果一个事件都没有就阻塞
selector.select();
//定义一个byte缓冲区来存储收发的数据
ByteBuffer echoBuffer=ByteBuffer.allocate(10);
//处理数据
this.accept(selector,echoBuffer);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 处理数据
* @param selector
* @param echoBuffer
*/
public void accept(Selector selector,ByteBuffer echoBuffer){
SocketChannel sc;
try{
//此循环遍例所有产生的事件
for (SelectionKey key : selector.selectedKeys()){
//如果产生的事件为接受客户端连接(当有客户端连接服务器的时候产生)
if((key.readyOps()&SelectionKey.OP_ACCEPT)==SelectionKey.OP_ACCEPT){
selector.selectedKeys().remove(key);
//定义一个服务器socket通道
ServerSocketChannel subssc=(ServerSocketChannel)key.channel();
//将临时socket对象实例化为接收到的客户端的socket
sc=subssc.accept();
//将客户端的socket设置为异步
sc.configureBlocking(false);
//将客户端的socket的读取事件注册到事件选择器中
sc.register(selector, SelectionKey.OP_READ);
//将本此事件从迭带器中删除
System.out.println("有新连接:"+sc);
//如果产生的事件为读取数据(当已连接的客户端向服务器发送数据的时候产生)
}else if((key.readyOps()&SelectionKey.OP_READ)==SelectionKey.OP_READ){
//将本次事件删除
selector.selectedKeys().remove(key);
//临时socket对象实例化为产生本事件的socket
sc=(SocketChannel) key.channel();
//定义一个用于存储byte数据的流对象
ByteArrayOutputStream bos=new ByteArrayOutputStream();
//先将客户端的数据清空
echoBuffer.clear();
//a为读取到数据的长度
try{
//循环读取所有客户端数据到byte缓冲区中,当有数据的时候read函数返回数据长度
//NIO会自动的将缓冲区一次容纳不下的自动分段
int readInt=0;
while((readInt=sc.read(echoBuffer)) > 0){
//如果获得数据长度比缓冲区大小小的话
if(readInt<echoBuffer.capacity()){
//建立一个临时byte数组,将齐长度设为获取的数据的长度
byte[] readByte=new byte[readInt];
//循环向此临时数组中添加数据
for(int i=0;i<readInt;i++){
readByte[i]=echoBuffer.get(i);
}
//将此数据存入byte流中
bos.write(readByte);
}else{
//将读取到的数据写入到byte流对象中
bos.write(echoBuffer.array());
}
//将缓冲区清空,以便进行下一次存储数据
echoBuffer.clear();
}
//当循环结束时byte流中已经存储了客户端发送的所有byte数据
System.out.println("接收数据: "+new String(bos.toByteArray()));
}catch(Exception e){
//当客户端在读取数据操作执行之前断开连接会产生异常信息
e.printStackTrace();
//将本socket的事件在选择器中删除
key.cancel();
break;
}
//获取byte流对象的标准byte对象
byte[] b=bos.toByteArray();
//建立这个byte对象的ByteBuffer,并将数据存入
ByteBuffer byteBuffer=ByteBuffer.allocate(b.length);
byteBuffer.put(b);
//向客户端写入收到的数据
Write(byteBuffer,sc);
//关闭客户端连接
sc.close();
//将本socket的事件在选择器中删除
key.cancel();
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("连接结束");
System.out.println("=============================");
}
}
/**
* 写数据
* @param echoBuffer
* @param sc
* @return
*/
public boolean Write(ByteBuffer echoBuffer,SocketChannel sc){
//将缓冲区复位以便于进行其他读写操作
echoBuffer.flip();
try{
//向客户端写入数据,数据为接受到数据
sc.write(echoBuffer);
}catch (IOException e){
e.printStackTrace();
return false;
}
System.out.println("返回数据: "+new String(echoBuffer.array()));
return true;
}
/**
* 启动服务
* @param args
*/
public static void main(String[] args) {
NioServer nioServer=new NioServer();
nioServer.accept();
}
}
客户端代码
package com.li.springboot.nio.client;
import java.io.ByteArrayOutputStream;
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;
/**
* nio通信客户端
* @author 张宗海
*
*/
public class NioClient {
/**
* 发送
* @param mssage
*/
public void send(String mssage){
try {
//定义一个记录套接字通道事件的对象
Selector selector = Selector.open();
//定义一个服务器地址的对象
SocketAddress address = new InetSocketAddress("localhost",8083);
//定义异步客户端
SocketChannel client=SocketChannel.open(address);
//将客户端设定为异步
client.configureBlocking(false);
//在轮讯对象中注册此客户端的读取事件(就是当服务器向此客户端发送数据的时候)
client.register(selector, SelectionKey.OP_READ);
//定义用来存储发送数据的byte缓冲区
ByteBuffer sendbuffer=ByteBuffer.allocate(mssage.length());
//定义用于接收服务器返回的数据的缓冲区
ByteBuffer readBuffer=ByteBuffer.allocate(mssage.length());
//将数据put进缓冲区
sendbuffer.put(mssage.getBytes("utf-8"));
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
//向服务器发送数据
client.write(sendbuffer);
System.out.println("发送数据: "+new String(sendbuffer.array()));
//利用循环来读取服务器发回的数据
while(true){
//如果客户端连接没有打开就退出循环
if(!client.isOpen()) break;
//此方法为查询是否有事件发生如果没有就阻塞,有的话返回事件数量
int shijian=selector.select();
//如果没有事件返回循环
if(shijian==0){
continue;
}
this.send(selector,readBuffer,client);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 发送数据
* @param selector
* @param readBuffer
* @param client
*/
public void send(Selector selector,ByteBuffer readBuffer,SocketChannel client){
try{
//遍例所有的事件
for (SelectionKey key : selector.selectedKeys()){
//删除本次事件
selector.selectedKeys().remove(key);
//如果本事件的类型为read时,表示服务器向本客户端发送了数据
if (key.isReadable()){
//将临时客户端对象实例为本事件的socket对象
SocketChannel sc=(SocketChannel) key.channel();
//定义一个用于存储所有服务器发送过来的数据
ByteArrayOutputStream bos=new ByteArrayOutputStream();
//将缓冲区清空以备下次读取
readBuffer.clear();
//此循环从本事件的客户端对象读取服务器发送来的数据到缓冲区中
while(sc.read(readBuffer) > 0){
//将本次读取的数据存到byte流中
bos.write(readBuffer.array());
//将缓冲区清空以备下次读取
readBuffer.clear();
}
//如果byte流中存有数据
if(bos.size()>0){
//建立一个普通字节数组存取缓冲区的数据
byte[] b=bos.toByteArray();
System.out.println("接收数据: " + new String(b));
//关闭客户端连接,此时服务器在read读取客户端信息的时候会返回-1
client.close();
System.out.println("连接关闭!");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 启动
* @param args
*/
public static void main(String[] args) {
NioClient nioClient=new NioClient();
nioClient.send("123456789");
}
}
测试结果
服务端控制台输出
客户端控制台输出