Netty入門——客戶端與服務端通訊
Netty簡介
Netty是一個基於JAVA NIO 類庫的非同步通訊框架,它的架構特點是:非同步非阻塞、基於事件驅動、高效能、高可靠性和高可定製性。換句話說,Netty是一個NIO框架,使用它可以簡單快速地開發網路應用程式,比如客戶端和服務端的協議。Netty大大簡化了網路程式的開發過程比如TCP和UDP的 Socket的開發。Netty 已逐漸成為 Java NIO 程式設計的首選框架。
什麼是物聯網?
nio通訊框架
物聯網主要運用到netty哪些特性
a、TCP長連線
b、能夠和各種序列化框架完美整合
為什麼要使用netty,相對於其他通訊框架mina有哪些優點
a、API使用簡單,開發門檻低
b、功能強大,預置了多種編解碼功能,支援多種主流協議
c、社群活躍,版本更新快
d、技術穩定可靠,如:elasticsearch、spark、dubbo、motan等開源框架底層通訊採用的是netty
1、Netty服務端編寫
首先,Netty通過ServerBootstrap啟動服務端程式碼,需要四步:
第一步,定義兩個執行緒組,用來處理客戶端通道的accept和讀寫事件
第二步,繫結服務端通道NioServerSocketChannel
第三步,給讀寫事件的執行緒通道繫結handle,處理具體的業務邏輯
第四步,繫結監聽
1.1、NettyServer——Netty服務端編寫
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
/**
* Netty服務端編寫
*
* @author Administrator
*
*/
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
// 首先,netty通過ServerBootstrap啟動服務端
ServerBootstrap server = new ServerBootstrap();
EventLoopGroup parentGroup = new NioEventLoopGroup();
EventLoopGroup childGroup =new NioEventLoopGroup();
//第1步定義兩個執行緒組,用來處理客戶端通道的accept和讀寫事件
//parentGroup用來處理accept事件,childgroup用來處理通道的讀寫事件
//parentGroup獲取客戶端連線,連線接收到之後再將連線轉發給childgroup去處理
server.group(parentGroup, childGroup);
//用於構造服務端套接字ServerSocket物件,標識當伺服器請求處理執行緒全滿時,用於臨時存放已完成三次握手的請求的佇列的最大長度。
//用來初始化服務端可連線佇列
//服務端處理客戶端連線請求是按順序處理的,所以同一時間只能處理一個客戶端連線,多個客戶端來的時候,服務端將不能處理的客戶端連線請求放在佇列中等待處理,backlog引數指定了佇列的大小。
server.option(ChannelOption.SO_BACKLOG, 128);
//第2步繫結服務端通道
server.channel(NioServerSocketChannel.class);
//第3步繫結handler,處理讀寫事件,ChannelInitializer是給通道初始化
server.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//解碼器,接收的資料進行解碼,一定要加在SimpleServerHandler 的上面
//maxFrameLength表示這一貞最大的大小
//delimiter表示分隔符,我們需要先將分割符寫入到ByteBuf中,然後當做引數傳入;
//需要注意的是,netty並沒有提供一個DelimiterBasedFrameDecoder對應的編碼器實現(筆者沒有找到),因此在傳送端需要自行編碼新增分隔符,如 \r \n分隔符
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()[0]));
//把傳過來的資料 轉換成byteBuf
ch.pipeline().addLast(new SimpleServerHandler());
}
});
//第4步繫結8080埠
ChannelFuture future = server.bind(8080).sync();
//當通道關閉了,就繼續往下走
future.channel().closeFuture().sync();
}
}
1.2、SimpleServerHandler——處理客戶端返回的資料
import java.nio.charset.Charset;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* 處理客戶端返回的資料
*
* @author Administrator
*
*/
public class SimpleServerHandler extends ChannelInboundHandlerAdapter {
/**
* 讀取客戶端通道的資料
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//可以在這裡面寫一套類似SpringMVC的框架
//讓SimpleServerHandler不跟任何業務有關,可以封裝一套框架
if(msg instanceof ByteBuf){
System.out.println(((ByteBuf)msg).toString(Charset.defaultCharset()));
}
//業務邏輯程式碼處理框架。。。
//返回給客戶端的資料,告訴我已經讀到你的資料了
String result = "hello client ";
ByteBuf buf = Unpooled.buffer();
buf.writeBytes(result.getBytes());
ctx.channel().writeAndFlush(buf);
ByteBuf buf2 = Unpooled.buffer();
buf2.writeBytes("\r\n".getBytes());
ctx.channel().writeAndFlush(buf2);
System.out.println("==========");
}
}
//使用下面命令測試上面程式碼。
1.開啟上面main方法。
2.執行一下命令
c:>telnet localhost 8080
隨便輸入,會發現eclipse控制檯會監聽到輸入的內容,有個問題接收時一個字一個字接受,可以讓他一行一行接收
2、netty客戶端編寫
第一步,連線到服務端
第二步,向服務端傳送資料
第三步,處理服務端返回的資料
第四步,關閉連線
2.1、NettyClient——Netty客戶端編寫
import com.alibaba.fastjson.JSONObject;
import com.dxfx.user.model.User;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.AttributeKey;
/**
* Netty客戶端編寫
* @author Administrator
*
*/
public class NettyClient {
public static void main(String[] args) throws InterruptedException {
// 首先,netty通過ServerBootstrap啟動服務端
Bootstrap client = new Bootstrap();
//第1步 定義執行緒組,處理讀寫和連結事件,沒有了accept事件
EventLoopGroup group = new NioEventLoopGroup();
client.group(group );
//第2步 繫結客戶端通道
client.channel(NioSocketChannel.class);
//第3步 給NIoSocketChannel初始化handler, 處理讀寫事件
client.handler(new ChannelInitializer<NioSocketChannel>() { //通道是NioSocketChannel
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
//字串編碼器,一定要加在SimpleClientHandler 的上面
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(
Integer.MAX_VALUE, Delimiters.lineDelimiter()[0]));
//找到他的管道 增加他的handler
ch.pipeline().addLast(new SimpleClientHandler());
}
});
//連線伺服器
ChannelFuture future = client.connect("localhost", 8080).sync();
//傳送資料給伺服器
User user = new User();
user.setAge(12);
user.setId(1);
user.setName("sssss");
future.channel().writeAndFlush(JSONObject.toJSONString(user)+"\r\n");
for(int i=0;i<5;i++){
String msg = "ssss"+i+"\r\n";
future.channel().writeAndFlush(msg);
}
//當通道關閉了,就繼續往下走
future.channel().closeFuture().sync();
//接收服務端返回的資料
AttributeKey<String> key = AttributeKey.valueOf("ServerData");
Object result = future.channel().attr(key).get();
System.out.println(result.toString());
}
}
2.2、SimpleClientHandler——處理服務端返回的資料
import java.nio.charset.Charset;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;
/**
* 處理服務端返回的資料
*
* @author Administrator
*
*/
public class SimpleClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
String value = ((ByteBuf) msg).toString(Charset.defaultCharset());
System.out.println("伺服器端返回的資料:" + value);
}
AttributeKey<String> key = AttributeKey.valueOf("ServerData");
ctx.channel().attr(key).set("客戶端處理完畢");
//把客戶端的通道關閉
ctx.channel().close();
}
}
3、netty服務端輸出的資訊
{"age":12,"id":1,"name":"sssss"}
==========
ssss0
==========
ssss1
==========
ssss2
==========
ssss3
==========
ssss4
==========
4、netty客戶端輸出的資訊
伺服器端返回的資料:hello client
客戶端處理完畢
伺服器端返回的資料:hello client
伺服器端返回的資料:hello client
伺服器端返回的資料:hello client
伺服器端返回的資料:hello client
伺服器端返回的資料:hello client