今天先介绍NioSocket的基本用法,实际使用一般会采用多线程,后面会介绍多线程的处理方法。

从jdk1.4开始,java增加了新的io模式--nio(​new IO​),nio在底层采用了新的处理方式,极大地提高了IO效率。我们使用的Socket也属于IO的一种,nio提供了相应的工具:​ServerSocketChanner​和​SocketChannel​,它们分别对应ServerSocket和Socket。(不了解java socket可以百度下)

NioSocket包括三个重要的概念:​Buffer​(类似于送快递中的货物)、​Channel​(类似于送快递中的送货车)和​Selector​(类似于送快递中转站的分拣员)。原来的Socket类似于一个人送快递,没有形成产业链。

下面来看下代码:

服务端

package com.test;

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.util.Iterator;

public class NIOServer {

public static void main(String[] args) throws Exception {

//创建ServerSocketChannel,监听8080端口

ServerSocketChannel ssc =  ​ServerSocketChannel.open();

ssc.socket().bind(new InetSocketAddress(8080));

//设置为非阻塞模式

ssc.configureBlocking(false);

//为ServerSocketChannel注册选择器

Selector selector = ​Selector.open();

/**

* 这里有四种SelectionKey分别表示接受请求操作、链接操作、读操作和写操作

* SelectionKey.OP_ACCEPT

* SelectionKey.OP_CONNECT

* SelectionKey.OP_READ

* SelectionKey.OP_WRITE

*/

ssc.register(selector, SelectionKey.OP_ACCEPT);

//创建处理器

Handler handler = new Handler(1024);//​handler的创建在下面静态的内部类中

while(true){

//等待请求,每次等待阻塞3s,超过3s后线程继续向下运动,如果传入0或者不传入参数将一直阻塞

if(selector.select(3000) == 0){

System.out.println("等待请求超时....");

continue;

}

System.out.println("处理请求......");

//获取待处理的SelectionKey

Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

while(keyIter.hasNext()){

SelectionKey key = keyIter.next();

try {

//​接收到链接请求时

if(key.isAcceptable()){

handler.handAccept(key);//​改变Selector为读操作

}

//​读数据

if(key.isReadable()){

handler.handlerRead(key);

}

} catch (Exception e) {

keyIter.remove();

continue;

}

//处理完成后,从待处理的SelectionKey迭代器中​移除当前所使用的key

keyIter.remove();

}

}

}


public static class Handler{

private int bufferSize = 1024;

private String localCharset = "UTF-8";

public Handler() {}

public Handler(int bufferSize) {

this(bufferSize,null);

}

public Handler(String localCharset) {

this(-1,localCharset);

}

public Handler(int bufferSize, String localCharset) {

if(bufferSize>0){

this.bufferSize = bufferSize;

}

if(localCharset != null){

this.localCharset = localCharset;

}

}

public void handAccept(SelectionKey key) throws IOException {

SocketChannel sc = ​((ServerSocketChannel)key.channel()).accept();

sc.configureBlocking(false);

sc.register(key.selector(), ​SelectionKey.OP_READ,​ ByteBuffer.allocate(bufferSize));//​改变Selector为读操作

}

public void handlerRead(SelectionKey key) throws IOException {

//获取channel

SocketChannel sc = ​(SocketChannel) key.channel();

//获取channel并重置

ByteBuffer buffer = ​(ByteBuffer) key.attachment();

buffer.clear();

//没有读到内容则关闭

if(sc.read(buffer)==-1){

sc.close();

}else{

//将buffer转成读状态

buffer.flip();

//将buffer中接收到的值按localCharset格式编码后保存到receivedString

String receivedString = ​Charset.forName(localCharset).newDecoder().decode(buffer).toString();

System.out.println("received from client: " + receivedString);

//返回数据给客户端

String sendString = "received data: " + receivedString;

buffer = ​ByteBuffer.wrap(sendString.getBytes(localCharset));

sc.write(buffer);

//关闭socket

sc.close();

}

}

}

}