传统的Socket IO 比较
传送IO特点
-
阻塞点
server.accept();
inputStream.read(bytes); -
单线程情况下只能有一个客户端
用线程池可以有多个客户端连接,但是非常消耗性能
NIO的特点
ServerSocketChannel ServerSocket
SocketChannel Socket
Selector
SelectionKey
NIO的一些疑问
- 客户端关闭的时候会抛出异常,死循环
解决方案
int read = channel.read(buffer);
if(read > 0){
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("服务端收到信息:" + msg);
//回写数据
ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes());
channel.write(outBuffer);// 将消息回送给客户端
}else{
System.out.println("客户端关闭");
key.cancel();
}
-
selector.select();阻塞,那为什么说nio是非阻塞的IO?
selector.select()
selector.select(1000);不阻塞
selector.wakeup();也可以唤醒selector
selector.selectNow();也可以立马返还 -
SelectionKey.OP_WRITE是代表什么意思
OP_WRITE表示底层缓冲区是否有空间,是则响应返还true
netty可以运用在那些领域?
- 分布式进程通信
例如: hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x - CS IM通信
- 游戏服务器开发
最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x
1、netty服务端hello world案例
SimpleChannelHandler 处理消息接收和写
{
messageReceived接收消息
channelConnected新连接,通常用来检测IP是否是黑名单
channelDisconnected链接关闭,可以再用户断线的时候清楚用户的缓存数据等
}
package com.server;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
* netty服务端入门
*
*/
public class Server {
public static void main(String[] args) {
//服务类
ServerBootstrap bootstrap = new ServerBootstrap();
//boss线程监听端口,worker线程负责数据读写
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//设置niosocket工厂
bootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));
//设置管道的工厂
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new StringDecoder());//上游
pipeline.addLast("encoder", new StringEncoder());//下游
pipeline.addLast("helloHandler", new HelloHandler());
return pipeline;
}
});
bootstrap.bind(new InetSocketAddress(10101));
System.out.println("start!!!");
}
}
2、netty客户端hello world案例
channelDisconnected与channelClosed的区别?
channelDisconnected只有在连接建立后断开才会调用
channelClosed无论连接是否成功都会调用关闭资源
package com.client;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
* netty客户端入门
*
*/
public class Client {
public static void main(String[] args) {
//服务类
ClientBootstrap bootstrap = new ClientBootstrap();
//线程池
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//socket工厂
bootstrap.setFactory(new NioClientSocketChannelFactory(boss, worker));
//管道工厂
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("hiHandler", new HiHandler());//
return pipeline;
}
});
//连接服务端
ChannelFuture connect = bootstrap.connect(new InetSocketAddress("127.0.0.1", 10101));
Channel channel = connect.getChannel();//与 HiHandler 是统一个Channel,在这里与Hihandler 中处理一样
System.out.println("client start");
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("请输入");
channel.write(scanner.next());
}
}
}