1>. OIO 堵塞IO传输服务
2>. NIO 异步IO传输服务
我们通过一个简单的例子来开始我们的传输服务学习。这个例子很简单,服务端接收连接,发送一个Hi到客户端,然后关闭连接。
一、 使用JAVA原生API实现堵塞网络传输
package demo.simple;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
/**
*
* Blocking networking without Netty
* @author royal
*
*/
public class PlainOioServer {
public void serve(int port) throws IOException {
// 绑定服务器到指定的端口
final ServerSocket socket = new ServerSocket(port);
try {
for (;;) {
// 接受一个连接
final Socket clientSocket = socket.accept();
System.out.println("Accepted connection from " + clientSocket);
// 创建一个新的线程来处理连接
new Thread(new Runnable() {
@Override
public void run() {
OutputStream out;
try {
out = clientSocket.getOutputStream();
// 将消息发送到连接的客户端
out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")));
out.flush();
// 一旦消息被写入和刷新时就 关闭连接
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
try {
clientSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}).start(); // 启动线程
}
} catch (Exception e) {
e.printStackTrace();
} finally {
socket.close();
}
}
}
二、使用JAVA原生API实现非堵塞网络传输
package demo.simple;
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;
/**
* 应用程序什么都不做,只是接受客户端连接并发送“Hi!”字符串消息到客户端,发送完了就断开连接。 Asynchronous networking
* without Netty
*
* @author royal
*
*/
public class PlainNioServer {
public void serve(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
// 绑定服务器到制定端口
ss.bind(address);
// 打开 selector 处理 channel
Selector selector = Selector.open();
// 注册 ServerSocket 到 ServerSocket,并指定这是专门接受 连接
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
for (;;) {
// 等待新的事件来处理。这将阻塞,直到一个事件是传入
selector.select();
// 从收到的所有事件中 获取 SelectionKey 实例
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
// 检查该事件是一个新的连接准备好接受
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 接受客户端,并用 selector 进行注册
client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate());
System.out.println("Accepted connection from " + client);
}
// 检查 socket 是否准备好写数据
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
while (buffer.hasRemaining()) {
// 将数据写入到所连接的客户端。如果网络饱和,连接是可写的,那么这个循环将写入数据,直到该缓冲区是空的
if (client.write(buffer) == 0) {
break;
}
}
// 关闭连接
client.close();
}
} catch (Exception e) {
key.cancel();
try {
key.channel().close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}
三、使用Netty实现堵塞网络传输
package demo.simple;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
public class NettyOioServer {
public void server(int port) throws Exception {
final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")));
// 用OioEventLoopGroup指定使用堵塞模式(Old I/O)
EventLoopGroup group = new OioEventLoopGroup();
try {
// 创建一个 ServerBootstrap
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(OioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
// 指定 ChannelInitializer给每个接受的连接调用
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 添加ChannelHandlerAdapter接收和处理事件
ch.pipeline().addLast(new ChannelHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 写信息到客户端,并添加 ChannelFutureListener 当一旦消息写入就关闭连接
ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
}
});
}
});
// 绑定服务器接受连接
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} finally {
// 释放资源
group.shutdownGracefully().sync();
}
}
}
四、使用Netty实现非堵塞网络传输
package demo.simple;
import java.nio.charset.Charset;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyNioServer {
public void server(int port) throws Exception {
final ByteBuf buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")));
// 用NioEventLoopGroup指定使用非堵塞模式
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
// 创建一个 ServerBootstrap
ServerBootstrap b = new ServerBootstrap();
b.group(boss, worker).channel(NioServerSocketChannel.class)
// 指定 ChannelInitializer 将给每个接受的连接调用
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 添加ChannelHandlerAdapter接收和处理事件
ch.pipeline().addLast(new ChannelHandlerAdapter() {
public void channelActive(ChannelHandlerContext ctx) {
// 写信息到客户端,并添加 ChannelFutureListener 当一旦消息写入就关闭连接
ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
}
});
}
});
// 绑定服务器来接受连接
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
// 释放资源
worker.shutdownGracefully().sync();
boss.shutdownGracefully().sync();
}
}
}