netty構建一個簡單的C/S程式
阿新 • • 發佈:2020-12-19
構建一個簡單的socket程式
這裡採用的是空maven專案,構建client端和server端,實現一個客戶端建立連線後傳送訊息,然後服務端返回一個訊息的簡單程式
server端程式碼
基本套路:
- 在Server類中建立分發執行緒組和工作執行緒組,建立啟動類Bootstrap服務端是ServerBootstrap)
- 為Bootstrap進行初始化,指定channel,初始化channel(初始化channel有兩個函式,一個是childHandler,還有個是handler,其中childHandler對應的是工作執行緒組)
- 編寫初始化channel的類,裝填handler在管道中。(這裡我把channel初始化類解除安裝Server類中了,使用了匿名內部類建立並初始化)
- 編寫業務handler
Server類程式碼:
public class Server { public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup();//分發執行緒組 EventLoopGroup workerGroup = new NioEventLoopGroup();//工作執行緒組 ServerBootstrap cb = new ServerBootstrap();//服務啟動物件 try { //這裡直接用匿名內部類構造channel初始化類 //繫結執行緒組,設定channel,初始化channel cb.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline();//初始化管道 pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)) .addLast(new LengthFieldPrepender(4)) .addLast(new StringDecoder()) .addLast(new StringEncoder()) .addLast(new ServerHandler());//最後執行我們的業務handler } }); ChannelFuture channelFuture = cb.bind( 9999).sync();//繫結埠 channelFuture.channel().closeFuture().sync();//關閉 } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully();//優雅關閉 workerGroup.shutdownGracefully();//優雅關閉 } } }
handler程式碼:
public class ServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { //等待五秒後傳送給客戶端訊息 Thread.sleep(5000); System.out.println(ctx.channel().remoteAddress()+":"+msg); ctx.writeAndFlush("服務端端回覆"); } //處理異常 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close();//遇到異常關閉連線 } }
client端程式碼
client端程式碼和server端程式碼很相似,這也是源於netty的巧妙設計,很強大
client端的server程式碼:
public class ClientServer {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();//客戶端不需要分發執行緒,所以只需要一個執行緒組就行了
Bootstrap cb = new Bootstrap();//這裡不再使用ServerBootstrap了,因為是客戶端
try {
//這裡直接用匿名內部類構造channel初始化類
//同樣這裡的channel物件,用的也不是NioServerSocketChannel進行反射構造了。因為只有一個執行緒組就使用了handler
cb.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4))
.addLast(new LengthFieldPrepender(4))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new ClientHandler());//這裡基本上和服務端一樣
}
});
ChannelFuture channelFuture = cb.connect("localhost", 9999).sync();
channelFuture.channel().writeAndFlush("hello");//這裡是手動傳送訊息,其實還可以通過handler中的回撥觸發傳送訊息
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
handler程式碼:
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(ctx.channel().remoteAddress()+":"+msg);//顯示服務端的訊息
ctx.writeAndFlush("客戶端回覆");//然後再回復,這裡就會不斷的相互發送訊息,因為服務端也會回覆,互相傳送
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
執行結果
先啟動server再啟動client,檢視結果
Server端
Client端
總結
可以看出netty不僅對服務端有很好的封裝,相應的對於服務端也能同樣的套路編寫。方便了我們使用底層的網路程式設計API,netty的設計理念只是為了提供一個更好的使用底層網路程式設計的框架,可以供我們做更多的上層搭建。
通過之前的基於Http協議的程式和現在的C/S模式的程式,也能看出,這其中變化最大的部分應該是對channel初始化的配置,其中很多都是使用netty提供好的handler進行管道的配置,能讓我們更容易的解析socket中傳輸的資料,這將會是使用netty的重點學習部分。