本系列文章主要来自《Netty IN ACTION》
本章主要内容:
Java网络编程
Netty简介
Netty核心组件
Java网络编程
首先来看一个典型的阻塞I/O示例:
//创建一个新的ServerSocket,用于监听指定端口上的连接请求
ServerSocket serverSocket = new ServerSocket(portNumber);//1
//以阻塞的方式来调用accept()方法,直到一个连接建立
Socket clientSocket = serverSocket.accept();//2
//BufferedReader和PrintWriter分别用于从字符输入流中读取文本和将对象格式化到文本输出流
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
String request,response;
//循环对输入流进行判断处理
while((request = in.readLine()) != null){
if("Done".equals(request)){
break;
}
//服务器处理客户端的请求,并作出响应
response = processRequest(request);
//输出响应
out.println(response);
这里ServerSocket
的作用是监听某一路端口是否有连接,如果有连接就会创建一个新的Socket
用于客户端和服务器进行通信,而此时ServerSocket
并不会断开连接,而是会继续监听传入的连接。
这里就体现出弊端了,如果有多个并发客户端,那就需要为每个客户端Socket创建一个Thread,弊端有三:
- 资源浪费,并不是每时每刻都有数据在传输,大部分情况下线程都处在休眠状态;
- 内存消耗,需要为每个线程的调度栈分配内存;
- 上下文切换带来的开销大;
Java NIO
使用setSockopt()方法配置套接字,以便读/写调用在没有数据的时候立即返回。
选择器
使用java.nio.channels.Selector
这个类,利用事件通知API来确定在一组非阻塞套接字中有哪些已经就绪能够进行I/O相关的操作。
Netty简介
Netty是一个NIO客户端/服务器框架,支持快速、简单地开发网络应用,如协议服务器和客户端。
Netty核心组件
Netty的主要构建块:
- Channel
- 回调
- Future
- 事件和Channelhandler
Channel
简单理解就是传入或者传出数据的载体,可以被打开或者被关闭,连接或者断开连接。
回调
回调其实就是一个方法,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。Netty内部使用了回调来处理事件,当一个回调被触发时,相关的事件被一个ChannelHandler的实现来处理,
看下面一段代码:
public class ConnectHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(channelHandlerContect ctx) throws Exception{
System.out.println("Client " + ctx.channel().remoteAddress() + " connected");
}
}
当一个新的连接已经被建立时,ChannelHandler的channelActive()回调方法将会被调用,并将打印一条信息。
Future
可以看作是一个异步操作的结果的占位符,可以在未来提供对其结果的访问;
Netty提供了自己的ChannelFuture,用于在执行异步操作的时候使用。
public class ConnectExample {
private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel();
public static void connect() {
Channel channel = CHANNEL_FROM_SOMEWHERE;
//异步地连接到远程节点
ChannelFuture future = channel.connect(
new InetSocketAddress("192.168.0.1", 25));
//注册一个 ChannelFutureListener,以便在操作完成时获得通知
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
//检查操作的状态
if (future.isSuccess()) {
//如果操作是成功的,则创建一个 ByteBuf 以持有数据
ByteBuf buffer = Unpooled.copiedBuffer(
"Hello", Charset.defaultCharset());
//将数据异步地发送到远程节点。返回一个 ChannelFuture
ChannelFuture wf = future.channel()
.writeAndFlush(buffer);
// ...
} else {
//如果发生错误,则访问描述原因的 Throwable
Throwable cause = future.cause();
cause.printStackTrace();
}
}
});
}
}
可以看到,回调和Future是相互补充的机制,而ChannelFutureListener可以看作是回调的一个更加精细的版本。
事件和Channelhandler
Netty使用不同的事件来通知我们状态的改变或者是操作的状态,因此我们可以利用事件来进行相应的处理,比如:
- 记录日志
- 数据转换
- 流控制
- 应用程序逻辑
Netty作为一个网络编程框架,主要事件分为入站事件和出站事件,入站事件包括:
- 连接已被激活或者连接失活
- 数据读取
- 用户事件
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!