您的位置:首页 > 其它

netty入门学习(1)-从NIO说起

2013-05-29 16:02 387 查看
学习netty之前,先来一个NIO的入门级示例(当然netty不仅仅对NIO进行了封装支持,对BIO同样也支持,本系统只针对NIO进行处理):

一:

服务端

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

public class ChargenServer {
public static int DEFAULT_PORT = 19;
public static void main(String[] args){
int port;
try{
port = Integer.parseInt(args[0]);
}catch(Exception ex){
port = DEFAULT_PORT;
}

System.out.println("Listening for connections on port " + port);

byte[] rotation = new byte[95*2];
for(byte i=' ';i<='~';i++){
rotation[i-' '] = i;
rotation[i+95-' '] = i;
}

ServerSocketChannel serverChannel;
Selector selector;
try{
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch(IOException ex){
ex.printStackTrace();
return;
}
while(true){
try{
selector.select();
}catch(IOException ex){
ex.printStackTrace();
break;
}
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while(iterator.hasNext()){
SelectionKey key = (SelectionKey)iterator.next();
iterator.remove();
try{
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
ByteBuffer buffer = ByteBuffer.allocate(74);
buffer.put(rotation,0,72);
buffer.put((byte)'\r');
buffer.put((byte)'\n');
buffer.flip();
key2.attach(buffer);
}else if(key.isWritable()){//测试此键的通道是否已准备好进行写入
SocketChannel client = (SocketChannel)key.channel();
ByteBuffer buffer = (ByteBuffer)key.attachment();
if(!buffer.hasRemaining()){
buffer.rewind();
int first = buffer.get();
buffer.rewind();
int position = first - ' ' + 1;
buffer.put(rotation,position,72);
buffer.put((byte)'\r');
buffer.put((byte)'\n');
buffer.flip();
}
client.write(buffer);
}
}
catch(IOException ex){
key.cancel();
try{
key.channel().close();
}catch(IOException cex){}
}
}
}
}
}


二:
客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;

public class ChargenClient {
public static int DEFAULT_PORT = 19;
public static void main(String[] args){
try{
SocketAddress address = new InetSocketAddress(DEFAULT_PORT);
SocketChannel client = SocketChannel.open(address);
ByteBuffer buffer=ByteBuffer.allocate(74);
WritableByteChannel out = Channels.newChannel(System.out);
while(client.read(buffer) != -1){
buffer.flip();
out.write(buffer);
buffer.clear();
}
}catch(IOException ex){
ex.printStackTrace();
}
}
}


在这个示例中有几点需要注意:

(1)服务端的写事件什么时候触发?服务在对accept事件进行处理时,将写事件注册到注册集合,JVM会根据服务端的TCP缓冲区(这个是内核态的,区别于ByteBuffer等用户态缓冲区)是否可写入(未满)来决定是否将写事件加入触发集合,即触发写操作。注意:应根据业务场景来决定是否注销写事件,如果不注销,很容易造成CPU空转。

当然也可以将触发写操作的业务代码写到对accpet事件的处理中,如下。但是这样做有可能因TCP缓冲区已满,而引起写阻塞,违背了NIO的初忠。

public static void main(String[] args){
int port;
try{
port = Integer.parseInt(args[0]);
}catch(Exception ex){
port = DEFAULT_PORT;
}

System.out.println("Listening for connections on port " + port);

byte[] rotation = new byte[95*2];
for(byte i=' ';i<='~';i++){
rotation[i-' '] = i;
rotation[i+95-' '] = i;
}

ServerSocketChannel serverChannel;
Selector selector;
try{
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch(IOException ex){
ex.printStackTrace();
return;
}
while(true){
try{
selector.select();
}catch(IOException ex){
ex.printStackTrace();
break;
}
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while(iterator.hasNext()){
SelectionKey key = (SelectionKey)iterator.next();
iterator.remove();
try{
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);

ByteBuffer buffer = ByteBuffer.allocate(74);
buffer.put(rotation,0,72);
buffer.put((byte)'\r');
buffer.put((byte)'\n');
buffer.flip();
client.write(buffer);
}
}
catch(IOException ex){
key.cancel();
try{
key.channel().close();
}catch(IOException cex){}
}
}
}

}


(2)在NIO模型中,服务端一次写操作,有可能会导致客户端多次读操作。同样,服务端多次写操作,也可能只会触发客户端一次读操作。所以在具体的业务操作编程时,需要对此种情况进行处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: