说明:这篇文章的code不是我写的,来自 林昊的分布式Java 基础与应用的示例demo.
server端:
package mytcpnio;
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;
import java.nio.charset.Charset;
public class ServerDemo {
public static void main(String[] args) throws Exception{
int port = 11111;
Selector selector=Selector.open();
ServerSocketChannel ssc=ServerSocketChannel.open();
ServerSocket serverSocket=ssc.socket();
//绑定监听端口
serverSocket.bind(new InetSocketAddress(port));
System.out.println("Server listens on port: "+port);
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
int keysNum = selector.select(1000);
if(keysNum>0){
for(SelectionKey sk : selector.selectedKeys()){
if(sk.isAcceptable()){
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)sk.channel();
SocketChannel sc = serverSocketChannel.accept();
if(sc == null){
continue;
}
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
if(sk.isReadable()){
ByteBuffer buffer=ByteBuffer.allocate(1024);
SocketChannel sc=(SocketChannel) sk.channel();
int readBytes=0;
String message=null;
try{
int ret;
try{
while((ret=sc.read(buffer))>0){
readBytes+=ret;
}
}
catch(Exception e){
readBytes=0;
// IGNORE
}
finally{
buffer.flip();
}
if(readBytes>0){
message=Charset.forName("UTF-8").decode(buffer).toString();
buffer = null;
}
}
finally{
if(buffer!=null){
buffer.clear();
}
}
if(readBytes>0){
System.out.println("Message from client: "+ message);
if("quit".equalsIgnoreCase(message.trim())){
sc.close();
selector.close();
System.out.println("Server has been shutdown!");
System.exit(0);
}
String outMessage="Server response:"+message;
//将消息写入通道
sc.write(Charset.forName("UTF-8").encode(outMessage));
}
}
}
//清除这次循环的所有selectedKeys
selector.selectedKeys().clear();
}
}
}
}
分析:这个示例是一个基于tcp的通信demo.server端的大致创建过程如下:建立servesocketchannel,绑定监听端口,向selector注册,而后由selector轮询管理。server端只存在 accpetable和readable两种信道。
package mytcpnio;
import java.io.BufferedReader;
import java.io.InputStreamReader;
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;
import java.nio.charset.Charset;
public class ClientDemo {
public static void main(String[] args) throws Exception{
int port=11111;
SocketChannel socketChannal=SocketChannel.open();
socketChannal.configureBlocking(false);
SocketAddress target=new InetSocketAddress("127.0.0.1",port);
socketChannal.connect(target);
Selector selector=Selector.open();
socketChannal.register(selector, SelectionKey.OP_CONNECT);
BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in));
while(true){
if(socketChannal.isConnected()){
String command=systemIn.readLine();
/**写入消息到通道
* 退出命令 执行过程大致如下 将退出命令写入通道
*/
socketChannal.write(Charset.forName("UTF-8").encode(command));
if(command==null || "quit".equalsIgnoreCase(command.trim())){
systemIn.close();
socketChannal.close();
selector.close();
System.out.println("Client quit!");
System.exit(0);
}
}
int keysNum=selector.select(1000);
if(keysNum>0){
for (SelectionKey sk : selector.selectedKeys()) {
if(sk.isConnectable()){
SocketChannel sc=(SocketChannel) sk.channel();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
sc.finishConnect();
}
else if(sk.isReadable()){
ByteBuffer buffer=ByteBuffer.allocate(1024);
SocketChannel sc=(SocketChannel) sk.channel();
int readBytes=0;
try{
int ret=0;
try{
while((ret=sc.read(buffer))>0){
readBytes+=ret;
}
}
finally{
buffer.flip();
}
if(readBytes>0){
System.out.println(Charset.forName("UTF-8").decode(buffer).toString());
buffer = null;
}
}
finally{
if(buffer!=null){
buffer.clear();
}
}
}
}
selector.selectedKeys().clear();
}
}
}
}
client端的建立过程与server端大致相同,除了需要ip外,就是信道的种类不同,建立之后也是通过轮询来监听,client端只存在connectable和readable的信道。