傳統的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());
}
}
}