目录
在 Netty网络编程实战1,搭建第一个Netty服务器中,使用curl作为客户端访问,下面将通过Netty实现客户端,客户端代码依然采用Netty老套路
主程序类+自定义初始化器+自定义处理器
三部分组成。
一、服务端
1、主程序类
package com.guor.demo.netty.chat;
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 MyNettyServerTest {
public static void main(String[] args) throws InterruptedException {
/**
* EventLoopGroup:事件循环组,是一个线程池,也是一个死循环,用于不断地接收用户请求;
* serverGroup:用户监听及建立连接,并把每一个连接抽象为一个channel,最后再将连接交给clientGroup处理;
* clientGroup:真正的处理连接
*/
EventLoopGroup serverGroup = new NioEventLoopGroup();
EventLoopGroup clientGroup = new NioEventLoopGroup();
try {
// 服务端启动时的初始化操作
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 1、将serverGroup和clientGroup注册到服务端的Channel上;
// 2、注册一个服务端的初始化器MyNettyServerInitializer;
// 3、该初始化器中的initChannel()方法会在连接被注册到Channel后立刻执行;
// 5、最后将端口号绑定到8080;
ChannelFuture channelFuture = serverBootstrap.group(serverGroup, clientGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MyNettyServerInitializer()).bind(8080).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
System.out.println(e);
}finally {
serverGroup.shutdownGracefully();
clientGroup.shutdownGracefully();
}
}
}
2、自定义初始化器
package com.guor.demo.netty.chat;
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 MyNettyServerInitializer extends ChannelInitializer<SocketChannel> {
// 连接被注册到Channel后,立刻执行此方法
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/**
* LengthFieldBasedFrameDecoder用于解析带固定长度的数据报。
* TCP发送的数据规则:可以将数据进行拆分或合并,因此对端接收到的数据报可能不是初始发送时的格式;
* 一般的做法是在包头设置length长度,指明数据包的长度,再由接受方根据length拼接或剪裁收到的数据,从而形成完整的数据包
*/
pipeline.addLast("LengthFieldBasedFrameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,8,0,8));
// 将上条语句的length加入传递的数据中心
pipeline.addLast("LengthFieldPrepender",new LengthFieldPrepender(8));
// 传递字符串的编码解码器
pipeline.addLast("StringDecoder",new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("StringEecoder",new StringEncoder(CharsetUtil.UTF_8));
// 增加自定义处理器MyNettyServerHandler,用于实际处理请求,并给出响应
pipeline.addLast("MyNettyServerHandler",new MyNettyServerHandler());
}
}
3、自定义处理器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Scanner;
/**
* 自定义处理器
* Inbound代表"进入"的请求
*/
public class MyNettyServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String receiveMsg) throws Exception {
// 通过ctx获取客户端的IP和端口号,并打印出客户端发来的消息
System.out.println("服务端接收的请求来自:"+ctx.channel().remoteAddress()+",消息内容:"+receiveMsg);
System.out.println("请向客户端发送一条消息:");
String sendMsg = new Scanner(System.in).nextLine();
ctx.channel().writeAndFlush(sendMsg);
}
}
二、客户端
1、主程序类
package com.guor.demo.netty.chat;
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 MyNettyClientTest {
public static void main(String[] args) {
/**
* 服务端有两个EventLoopGroup,serverGroup用于获取连接并将连接分发给clientGroup,clientGroup负责处理连接;
* 对于客户端而言,客户端仅仅需要连接服务端即可
*/
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
// 客户端启动时的初始化操作
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new MyNettyClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
System.out.println(e);
}finally {
eventLoopGroup.shutdownGracefully();
}
}
}
2、自定义初始化器
package com.guor.demo.netty.chat;
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 MyNettyClientInitializer extends ChannelInitializer<SocketChannel> {
// 连接被注册后,立即执行此方法
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
/**
* LengthFieldBasedFrameDecoder用于解析带固定长度的数据报。
* TCP发送的数据规则:可以将数据进行拆分或合并,因此对端接收到的数据报可能不是初始发送时的格式;
* 一般的做法是在包头设置length长度,指明数据包的长度,再由接受方根据length拼接或剪裁收到的数据,从而形成完整的数据包
*/
pipeline.addLast("LengthFieldBasedFrameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,8,0,8));
// 将上条语句的length加入传递的数据中心
pipeline.addLast("LengthFieldPrepender",new LengthFieldPrepender(8));
// 传递字符串的编码解码器
pipeline.addLast("StringDecoder",new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("StringEecoder",new StringEncoder(CharsetUtil.UTF_8));
// 增加自定义处理器MyNettyClientHandler
pipeline.addLast("MyNettyClientHandler",new MyNettyClientHandler());
}
}
3、自定义处理器
package com.guor.demo.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Scanner;
/**
* 自定义处理器
*/
public class MyNettyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String receiveMsg) throws Exception {
System.out.println("客户端接收的请求来自:"+ctx.channel().remoteAddress()+",消息内容:"+receiveMsg);
System.out.println("请向服务端发送一条消息:");
String sendMsg = new Scanner(System.in).nextLine();
ctx.channel().writeAndFlush(sendMsg);
}
public void channelActive(ChannelHandlerContext ctx) throws Exception{
ctx.writeAndFlush("第一条消息...");
}
}
三、启动服务端、客户端
1、服务端:你好,我是服务端,哪吒编程
2、客户端:我去,还真连上了,第一次使用Netty通话,真神奇
3、服务端:土包子
Java高并发编程实战系列文章
Java高并发编程实战2,原子性、可见性、有序性,傻傻分不清
Java高并发编程实战3,Java内存模型与Java对象结构
Java高并发编程实战4,synchronized与Lock底层原理