转载自http://blog.csdn.net/z69183787/article/details/52945595
自己做了点修改,将连接由客户端一方关闭
1. 公共类,处理传递中文字符时候的乱码问题
package cn.erong.mfo.utils;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
public class Constants {
public static int readCapacity = 1024;
public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();
public static ByteBuffer encoder(CharBuffer charbuffer) throws IOException{
return encoder.encode(charbuffer);
}
public static CharBuffer decoder(ByteBuffer buffer) throws IOException{
return decoder.decode(buffer);
}
}
java nio 中处理乱码的类charset ,创建 Charset charset = Charset.forName("指定编码格式")
charsetencoder将字符缓冲区,编码为字节缓存区
charsetdecoder 将字节缓存区,解码为字符缓冲区
2. 服务端
package cn.erong.mfo.utils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer extends Thread{
public static void main(String[] args) {
NioServer server = new NioServer("127.0.0.1", 23000);
server.start();
}
private InetSocketAddress address;
private ByteBuffer readBuffer;
private Selector selector;
public NioServer(){}
public NioServer(String localhost,int port){
address = new InetSocketAddress(localhost, port);
}
private void init(){
readBuffer = ByteBuffer.allocate(Constants.readCapacity);
try {
selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(address);
//注册到信道上
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
init();
while(true){
int num = selector.select();
if(num>0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey sk = iterator.next();
handlerKey(sk);
//注意将键移除
iterator.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handlerKey(SelectionKey sk) {
if(sk.isAcceptable()){
accept(sk);
}else if(sk.isReadable()){
readMsg(sk);
}
}
public void accept(SelectionKey sk) {
try {
ServerSocketChannel serverChannel = (ServerSocketChannel)sk.channel();
SocketChannel channel = serverChannel.accept();
if(channel!=null){
//将信道一定注册为非阻塞的
channel.configureBlocking(false);
channel.register(sk.selector(), SelectionKey.OP_READ);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void readMsg(SelectionKey sk) {
SocketChannel channel = null;
try {
channel = (SocketChannel) sk.channel();
readBuffer.clear();
int num = channel.read(readBuffer);
if(num>0){
//将数据取出来
readBuffer.flip();
System.out.println("接受到的数据:"+num+"字节");
CharBuffer buffer = Constants.decoder(readBuffer);
String recevieMsg = buffer.toString();
System.out.println(recevieMsg);
String answer = getAnswer(recevieMsg);
CharBuffer cbuffer = CharBuffer.wrap(answer.toCharArray());
channel.write(Constants.encoder(cbuffer));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void writeMsg(SelectionKey sk) throws IOException{
}
private String getAnswer(String question){
String answer = null;
switch(question){
case "who":
answer = "我是小娜\n";
break;
case "what":
answer = "我是来帮你解闷的\n";
break;
case "where":
answer = "我来自外太空\n";
break;
case "hi":
answer = "hello\n";
break;
case "bye":
answer = "88\n";
break;
default:
answer = "请输入 who, 或者what, 或者where";
}
return answer;
}
}
note:
1. 在将选择器注册到信道channel前,必须将channel设置为非阻塞,否则会抛出IllegalBlockingModeException
2. 对于信道的read,write等操作,都必须注意在此之前,操作对应的缓冲区使用clear(),flip()
read --->>> clear(): 将position设置为0,limit设置为capacity
write--->>> flip(); 将limit设置为position,position设置为0
decode(byteBuffer),会从字节缓存区中取出数据放到生成的charBuffer中,这样调用之前,也要调用
缓冲区的flip方法
3. 在对select.selectionkeys()进行遍历,操作每个key,之后,需要将key从键集中移除,否则会阻塞,无法获取新的连接。
4.不建议使用new String(byteBuffer.array()) 去解码缓存区,因为.array()返回的是整个缓存区的内容,而decoder是返回position到limit间的数据。
3. 客户端
package cn.erong.mfo.utils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class NioClient implements Runnable{
public static void main(String[] args) {
for(int i=0;i<5;i++){
Runnable client = new NioClient("127.0.0.1", 23000);
Thread t = new Thread(client);
t.start();
}
}
private BlockingQueue<String >words;
private Random random;
private InetSocketAddress address;
public NioClient (String localname,int port){
address = new InetSocketAddress(localname, port);
}
private void init(){
words = new ArrayBlockingQueue<>(5);
try {
words.put("hi");
words.put("who");
words.put("what");
words.put("where");
words.put("bye");
} catch (InterruptedException e) {
e.printStackTrace();
}
random = new Random();
}
private String getWord(){
return words.poll();
}
@Override
public void run() {
init();
SocketChannel channel = null;
Selector selector = null;
try {
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(address);
channel.register(selector, SelectionKey.OP_CONNECT);
boolean isOver = false;
while(!isOver){
int num = selector.select();
if(num>0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey sk = iterator.next();
if(sk.isConnectable()){
if(channel.isConnectionPending()){
if(channel.finishConnect()){
//只有成功连接,才能注册read事件
sk.interestOps(SelectionKey.OP_READ);
//向服务端写数据
channel.write(Constants.encoder(CharBuffer.wrap(getWord())));
}else{
sk.cancel();// 将这个键注销
}
}
}else if(sk.isReadable()){
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bnum = channel.read(buffer);
if(bnum>0){
buffer.flip();
CharBuffer cbuffer = Constants.decoder(buffer);
System.out.println(Thread.currentThread().getId()+"客户端收到数据:"+cbuffer.toString());
String word = getWord();
if(word != null){
channel.write(Constants.encoder(CharBuffer.wrap(word)));
}
else{
isOver = true;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
iterator.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(channel!=null){
channel.close();
}
if(selector!=null){
selector.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
demo下载路径:http://download.csdn.net/download/ditto_zhou/10045911