1.niosocket服务端
package com.feng.test.nioSocket;
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.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
* nioSocket服务端
* @author lenovo
*
*/
public class NioSocketServer extends Thread {
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private Charset charset = Charset.forName("utf-8");
private static final int port = 9092;
private ExecutorService executorService = Executors.newSingleThreadExecutor();
/**
* 构造,实现等待以及连接客户端
*/
public NioSocketServer() {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务端已启动,等待客户端连接 ,端口号: "+port);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NioSocketServer ns = new NioSocketServer();
ns.execute();
}
public void run() {
execute();
}
public void execute(){
try {
while(selector.select()>0){//
// 无论是否有读写事件发生,selector每隔1s被唤醒一次
// selector.select(1000);
// //阻塞,只有当至少一个注册的事件发生的时候才会继续.
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
for(SelectionKey sk : selectedKeys){
System.out.println("--------------------------------------进来这儿 1 ---------------------------------------------");
if(sk.isAcceptable()){//Tests whether this key's channel is ready to accept a new socket connection.
System.out.println("接受连接请求:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//通过ServerSocketChannel的accept创建SocketChannel实例
//完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
SocketChannel socketChannel = serverSocketChannel.accept();//Accepts a connection made to this channel's socket.
//设置为非阻塞的
socketChannel.configureBlocking(false);
//注册为读
socketChannel.register(selector, SelectionKey.OP_READ);
//将key对应的channel设置成准备接受其他请求
sk.interestOps(SelectionKey.OP_ACCEPT);
}
System.out.println("--------------------------------------进来这儿 2 ---------------------------------------------");
//读消息
if(sk.isReadable()){
System.out.println("接受客户端请求: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
SocketChannel socketChannel = (SocketChannel) sk.channel();
//如果可读状态时读取的数据为空,则判断服务端关闭输出
executorService.submit(new ServerThread(sk,socketChannel));
}
}
selectedKeys.clear();//Removes all of the elements from this set (optional operation). The set will be empty after this call returns.
try {
System.out.println("--------------------------------------进来这儿3 ---------------------------------------------");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class ServerThread implements Callable<String>{
private SelectionKey selectionKey;
private SocketChannel socketChannel;
private static final int bufferSize = 4096;
public ServerThread(SelectionKey selectionKey,SocketChannel socketChannel) {
this.selectionKey = selectionKey;
this.socketChannel = socketChannel;
}
public String call() throws Exception {
try{//***没有处理异常,结果在此新起线程里,新线程死掉,主线程接收不到异常信息
String content = "";
ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize);
while(socketChannel.read(byteBuffer) > 0){
byteBuffer.flip();//Flips this buffer. The limit is set to the current position and then the position is set to zero. If the mark is defined then it is discarded.
content +=charset.decode(byteBuffer);
}
System.out.println("接受的客户端信息:"+content);
//将sk对应的channel设置成准备下一次的数据读取
selectionKey.interestOps(SelectionKey.OP_READ);
//处理数据
if(!content.equals("")){
Map<String,String> map = handleAction(content,socketChannel);//1
//Map<String,String> map = handleAction2(content,socketChannel);//2
}
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 处理业务流程,一次处理完就结束
* @param content
* @param sc
* @return
*/
private Map<String, String> handleAction(String content,SocketChannel sc){
Map<String, String> result = new HashMap<String, String>();
//处理业务
if(content!=null&&!"".equals(content)){
String resultStr = content+"_"+System.currentTimeMillis()+""+"\r\n";
doWrite(sc,resultStr);
try {
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
result.put("result", "正常");
}else{
result.put("result", "参数错误");
}
return result;
}
/**
* 处理业务流程不间断
* @param content
* @param sc
* @return
*/
private Map<String, String> handleAction2(String content,SocketChannel sc){
Map<String, String> result = new HashMap<String, String>();
//处理业务
if(content!=null&&!"".equals(content)){
new Thread(new chuiliThread(sc,content)).start();
}else{
result.put("result", "参数错误");
}
return result;
}
/**
* 异步发送应答消息
* @param sc
* @param resultStr
*/
private void doWrite(SocketChannel sc,String resultStr){
System.out.println("开始往客户端返回数据:"+resultStr);
//将消息编码为字节数组
byte[] bytes= resultStr.getBytes();
//根据数组容量创建ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);//This method transfers the bytes remaining in the given source buffer into this buffer
writeBuffer.flip();
try {
//发送缓冲区的字节数组
sc.write(writeBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 一直保持连接不关闭,服务端不断产生数据往客户端发送
* @author Lenovo
*
*/
private class chuiliThread implements Runnable {
private String request ;
private SocketChannel sc;
public chuiliThread(SocketChannel sc ,String request) {
this.sc = sc;
this.request = request;
}
public void run() {
while(true){
String result = "nihao_"+request+"_"+(int)(Math.random()*100)+"\r\n";
doWrite(sc,result);
try {
Thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 异步发送应答消息
* @param sc
* @param resultStr
*
*/
private void doWrite(SocketChannel sc,String resultStr){
System.out.println("开始往客户端返回数据:"+resultStr);
//将消息编码为字节数组
byte[] bytes= resultStr.getBytes();
//根据数组容量创建ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);//This method transfers the bytes remaining in the given source buffer into this buffer
writeBuffer.flip();
try {
//发送缓冲区的字节数组
sc.write(writeBuffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.niosocket客户端
package com.feng.test.nioSocket;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* nioSocket贯通整个流程,观察这个特性
* @author lenovo
*
*/
public class NioSocketClient extends Thread{
private final int bufferSize = 4096 ;//分配缓冲内存大小
private Charset charset;//niocharset,字符编码
private InetSocketAddress address;
private Selector selector ;//客户端专用selector
private SocketChannel socketChannel;//通道
//方法重载,传入参数
public NioSocketClient(String ip,Integer port,String code) {
this.address = new InetSocketAddress(ip, port);//构造网络地址
if(code!=null&&!"".equals(code)){
charset = Charset.forName(code);
}else{
charset = Charset.forName("utf-8");
}
}
public static void main(String[] args) {
NioSocketClient nioSocketClient = new NioSocketClient("localhost",9092,"UTF-8");
nioSocketClient.sendReq("111");
}
@Override
public void run() {
sendReq("111");
}
public String sendReq(String param) {
String result = "";
try {
if(param!=null&&!"".equals(param)){
//定义检测socketChannel的selector对象
selector = Selector.open();
//连接服务器,打开通道
socketChannel = SocketChannel.open(address);
//通道设置成非阻塞模式
socketChannel.configureBlocking(false);
//向Selector注册通道,此socketChannel注册到selector中
socketChannel.register(selector, SelectionKey.OP_READ);//SelectionKey.OP_READ
FutureTask<String> future = new FutureTask<String>(new clientThread(selector));//1
//FutureTask<String> future = new FutureTask<String>(new clientThread2(selector));//2
new Thread(future).start();
socketChannel.write(charset.encode(param));//Writes a sequence of bytes to this channel from the given buffer.
if(future.get() != null){//Waits if necessary for the computation to complete, and then retrieves its result.
result = future.get();
System.out.println("客户端打印:"+result);
}
}else{
result = "传参错误";
}
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return result;
}
/**
* 客户端读取通道中数据处理线程,单条数据处理完就结束,服务端也只响应这一次请求
* @author lenovo
*
*/
private class clientThread implements Callable<String> {
private Selector selector;
private static final int bufferSize = 4096;
public clientThread(Selector selector) {
this.selector = selector;
}
/**
*
*/
public String call() {
String result = "";
int flag = 0;
try {
while(selector.select() > 0){// The number of keys, possibly zero, whose ready-operation sets were updated.
System.out.println("--------------------------------------进来这儿 1 ---------------------------------------------");
for(SelectionKey sk :selector.selectedKeys() ){//A token representing the registration of a SelectableChannel with a Selector.
System.out.println("--------------------------------------进来这儿 2 ---------------------------------------------");
selector.selectedKeys().remove(sk);
if(sk.isReadable()){//Tests whether this key's channel is ready for reading.
//从key里面获取可读通道
SocketChannel channel = (SocketChannel) sk.channel();
//数据缓冲区设置
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
byte[] data = buffer.array();
//channel.read(buffer);//Reads a sequence of bytes from this channel into the given buffer.
//如果可读状态时读取的数据为空,则判断服务端关闭输出
if(channel.read(buffer)>-1){//The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream
System.out.println("服务端关闭输出");
flag = 1;
channel.close();
selector.close();
}
result = new StringBuffer().append(new String(data).trim()).toString();
System.out.println("客户端接收到的结果:"+result);
//sk.interestOps(SelectionKey.OP_READ);//Sets this key's interest set to the given value.
Thread.sleep(1000);
}
}
if(flag==1){
break;
}
}
System.out.println("-----selector.close()---------");
selector.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
/**
* 客户端读取通道中数据处理线程,一直保持连接不关闭,服务端不断产生数据往客户端发送
* @author lenovo
*
*/
private class clientThread2 implements Callable<String> {
private Selector selector;
private static final int bufferSize = 4096;
public clientThread2(Selector selector) {
this.selector = selector;
}
/**
*
*/
public String call() {
String result = "";
int flag = 0;
try {
while(selector.select() > 0){// The number of keys, possibly zero, whose ready-operation sets were updated.
System.out.println("--------------------------------------进来这儿 1 ---------------------------------------------");
for(SelectionKey sk :selector.selectedKeys() ){//A token representing the registration of a SelectableChannel with a Selector.
System.out.println("--------------------------------------进来这儿 2 ---------------------------------------------");
selector.selectedKeys().remove(sk);
if(sk.isReadable()){//Tests whether this key's channel is ready for reading.
//从key里面获取可读通道
SocketChannel channel = (SocketChannel) sk.channel();
//数据缓冲区设置
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
byte[] data = buffer.array();
//channel.read(buffer);//Reads a sequence of bytes from this channel into the given buffer.
//如果可读状态时读取的数据为空,则判断服务端关闭输出
if(channel.read(buffer)>-1){//The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream
System.out.println("服务端关闭输出");
flag = 1;
//channel.close();
//selector.close();
}
result = new StringBuffer().append(new String(data).trim()).toString();
System.out.println("客户端接收到的结果:"+result);
//sk.interestOps(SelectionKey.OP_READ);//Sets this key's interest set to the given value.
Thread.sleep(1000);
}
}
/*if(flag==1){
break;
}*/
}
System.out.println("-----selector.close()---------");
selector.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
/**
* 关闭通道,选择器
*/
public void close() {
try {
if(socketChannel!=null&&socketChannel.isConnected()){
socketChannel.close();
}
if(selector!=null&&selector.isOpen()){
selector.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.详细请看代码以及注释