netty學習二:基於socket通訊的小demo
阿新 • • 發佈:2019-02-19
概述
上次寫了一篇netty學習一:用netty構造http服務的小demo
簡單介紹如何使用netty編寫http程式,而基於socket程式設計才是netty的強項。
服務端程式碼
package socket.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class SocketServer {
public static void main(String[] args) throws InterruptedException {
// 接收連線,但是不處理
EventLoopGroup parentGroup = new NioEventLoopGroup();
// 真正處理連線的group
EventLoopGroup childGroup = new NioEventLoopGroup();
try {
//載入Initializer
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class)
//這裡的childHandler是服務於childGroup的,如果直接使用
//handler方法新增處理器,則是服務於parentGroup的
.childHandler(new SocketServerInitializer());
//繫結監聽埠
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
}
finally {
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
}
package socket.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class SocketServerInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//1、新增解碼器,用於解釋二進位制內容
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
//2、編碼器,用於計算訊息的長度,並把訊息長度以二進位制的形式追加到訊息的前面
pipeline.addLast(new LengthFieldPrepender(4));
//3、socket程式設計中需要對字串進行編碼解碼
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//4、新增自定義處理器
pipeline.addLast(new SocketServerHandler());
}
}
package socket.server;
import java.util.UUID;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class SocketServerHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("請求來自:"+ctx.channel().remoteAddress() + ",內容:" + msg);
ctx.channel().writeAndFlush("from server:"+UUID.randomUUID());
}
}
客戶端程式碼
package socket.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
public class SocketClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//載入Initializer
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new SocketClientInitializer());
//連線服務端
ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
channelFuture.channel().closeFuture().sync();
}
finally {
eventLoopGroup.shutdownGracefully();
}
}
}
package socket.client;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class SocketClientInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//1、新增解碼器,用於解釋二進位制內容
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
//2、編碼器,用於計算訊息的長度,並把訊息長度以二進位制的形式追加到訊息的前面
pipeline.addLast(new LengthFieldPrepender(4));
//3、socket程式設計中需要對字串進行編碼解碼
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//4、新增自定義處理器
pipeline.addLast(new SocketClientHandler());
}
}
package socket.client;
import java.time.LocalDateTime;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class SocketClientHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("伺服器端地址:"+ctx.channel().remoteAddress());
System.out.println("client receive:"+msg);
ctx.channel().writeAndFlush("from client:"+LocalDateTime.now());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush("你好,服務端");
}
}
當客戶端於服務端之間的channel建立後,客戶端程式可以在channelActive方法中給服務端傳送訊息,
服務端接收到訊息後,觸發SocketServerHandler類的channelRead0方法,給客戶端傳送訊息,
客戶端接收到訊息後,觸發了SocketClientHandler類的channelRead0的方法,給服務端傳送訊息。
無限發下去。
csdn code 路徑
這個專案的原始碼放置在csdn code上,歡迎訪問。