前言
最近因为项目的需求需要使用到socket,于是就自己学习了一下简单的服务端和客户端的搭建,以及在项目中因为协议是用\n来区分的顺便来
试试DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()),是否是真的对回车有进行粘包处理。
服务端:
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 NettyService {
private static final int port = 60001; //设置服务端端口
//创建两个group,用于分工进行
private static EventLoopGroup bossGroup = new NioEventLoopGroup();
private static EventLoopGroup workerGroup = new NioEventLoopGroup();
private static ServerBootstrap sb = new ServerBootstrap();
/**
* Netty创建全部都是实现自AbstractBootstrap。
* 客户端的是Bootstrap,服务端的则是ServerBootstrap。
**/
public static void main(String[] args) throws InterruptedException {
try {
sb.group(bossGroup,workerGroup);
sb.channel(NioServerSocketChannel.class);
sb.childHandler(new NettyServerFilter()); //设置过滤器
// 服务器绑定端口监听
ChannelFuture f = sb.bind(port).sync();
System.out.println("服务器启动···");
// 监听服务器关闭监听
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程
}
}
}
服务端的过滤器:
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyServerFilter extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline ph = socketChannel.pipeline();
// 以("\n")为结尾分割的 解码器
ph.addLast("framer", new DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()));
// 解码和编码,应和客户端一致
ph.addLast("decoder", new StringDecoder());
ph.addLast("encoder", new StringEncoder());
ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑
}
}
服务端业务处理:
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.InetAddress;
import java.util.Date;
/**
* 逻辑处理的hangdler
*/
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
/**
* 接收消息,逻辑处理的方法
* @param channelHandlerContext
* @param msg
* @throws Exception
*/
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg)
throws Exception {
// 收到消息直接打印输出
System.out.println("服务端接受的消息 : " + msg);
if("quit".equals(msg)){//服务端断开的条件
channelHandlerContext.close();
}
// 返回客户端消息
for(int i=0;i<1000;i++){
Date date=new Date();
channelHandlerContext.writeAndFlush(date+"\n");
}
}
/**
* 建立连接返回消息
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! \n");
}
}
客户端:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.IOException;
public class NettyClient {
public static String host = "127.0.0.1"; //ip地址
public static int port = 60001; //端口
/// 通过nio方式来接收连接和处理连接
private static EventLoopGroup group = new NioEventLoopGroup();
private static Bootstrap bs = new Bootstrap();
private static Channel ch;
/**
* Netty创建全部都是实现自AbstractBootstrap。
* 客户端的是Bootstrap,服务端的则是 ServerBootstrap
**/
public static void main(String[] args) throws InterruptedException, IOException {
System.out.println("客户端成功启动...");
bs.group(group);
bs.channel(NioSocketChannel.class);
bs.handler(new NettyClientFilter());
// 连接服务端
ch = bs.connect(host, port).sync().channel();
star();
}
public static void star() throws IOException{
String str="Hello Netty";
ch.writeAndFlush(str+ "\r\n");
}
}
客户端过滤器:
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class NettyClientFilter extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline ph = socketChannel.pipeline();
//和服务端一样
ph.addLast("framer", new DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()));
ph.addLast("decoder", new StringDecoder());
ph.addLast("encoder", new StringEncoder());
ph.addLast("handler", new NettyClientHandler()); //客户端的逻辑
}
}
客户端逻辑处理器:
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyClientHandler extends SimpleChannelInboundHandler<String> {
private static int count=0;//计算次数
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("客户端接受的消息: " + msg+" "+(++count));
}
//
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("正在连接... ");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("连接关闭! ");
super.channelInactive(ctx);
}
}
有添加DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()):
服务端:
客户端:
服务端朝客户端返回了1000条+1条的数据
可以看出是没有粘包现象的!
未添加DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()):
服务端:
客户端:
只剩下978条信息,明显出现粘包的现象!