服务端
pom文件
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.60.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
channel初始化类
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;
import java.util.concurrent.CountDownLatch;
/**
* channel初始化类
* @version V1.0
* @file: com.yjrj.presalehouse.handler.NettyChannelInitializer
* @description:
* @author: wanggd
* @date 2021-04-06
*/
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {
private String response;
private CountDownLatch countDownLatch;
private String param;
private RequestClientHandler requestClientHandler;
public NettyChannelInitializer (String param){
this.param = param;
}
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
countDownLatch = new CountDownLatch(1);
requestClientHandler = new RequestClientHandler(param,this);
socketChannel.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
socketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
socketChannel.pipeline().addLast(requestClientHandler);
}
public String getResponse(){
try{
if(null!=countDownLatch){
countDownLatch.await();
}
}catch (InterruptedException exception){
exception.printStackTrace();
}
return response;
}
// 用于设置响应结果,并且做countDown操作,通知请求线程
public void setResponse(String response) {
this.response = response;
countDownLatch.countDown();
}
}
自定义业务处理handler
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义业务处理handler
* @version V1.0
* @file: com.yjrj.presalehouse.handler.RequestClientHandler
* @description: 自定义业务处理handler
* @author: wanggd
* @date 2021-04-06
*/
@Slf4j
public class RequestClientHandler extends ChannelInboundHandlerAdapter{
private String param;
private NettyChannelInitializer nettyChannelInitializer;
public RequestClientHandler(String param,NettyChannelInitializer nettyChannelInitializer){
this.param = param;
this.nettyChannelInitializer = nettyChannelInitializer;
}
/**
* 客户端连接上服务端之后会调用此方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("****channelActive成功****接收的参数为::"+param);
ctx.writeAndFlush(param);
}
/**
* 客户端读取服务端写入的数据
* @param ctx
* @param obj
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
if(null!=obj){
log.info("Receive server response : [" + obj.toString() + "]");
}
nettyChannelInitializer.setResponse(obj.toString());
}
/**
* 连接或者发送读取数据出现异常
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
super.exceptionCaught(ctx, cause);
}
}
向客户端发送消息,需要发送时直接调用此方法即可
import com.yjrj.presalehouse.handler.NettyChannelInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
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.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 接收自有项目消息,转发给监管平台
* @version V1.0
* @file: com.yjrj.presalehouse.client.NettyClient
* @description: 自定义业务处理handler
* @author: wanggd
* @date 2021-04-06
*/
@Component
@Slf4j
public class NettyClient {
@Value("${netty.port:8081}")
private int port;
@Value("${netty.host:127.0.0.1}")
private String host;
public static Bootstrap bootstrap = null;
public static SocketChannel socketChannel = null;
public static ChannelFuture future = null;
public Map<String,Bootstrap> map= new HashMap<>();
/**
* 向监管平台发送消息
* @param msg 接收到的自有项目传递过来的消息
* @return
* @throws InterruptedException
*/
public String sendMessage(String msg) throws InterruptedException {
if(!map.containsKey(host)){
log.info("\n\n\n\n\n\n-------------------------新建连接服务端:"+host+":"+port+"---------------------------");
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(eventLoopGroup);
bootstrap.remoteAddress(host, port);
// 设置超时时间
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15000);
map.put(host,bootstrap);
}else{
log.info("\n\n\n\n\n\n--------------------------已连接服务端:"+host+":"+port+"-----------------------------");
bootstrap = map.get(host);
}
NettyChannelInitializer customerChannelInitializer = new NettyChannelInitializer(msg);
bootstrap.handler(customerChannelInitializer);
future = bootstrap.connect(host, port).sync();
if (future.isSuccess()) {
socketChannel = (SocketChannel) future.channel();
log.info("远程服务器已经连接, 可以进行数据交换..");
}
return customerChannelInitializer.getResponse();
}
}
客户端
pom文件
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.60.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.1</version>
</dependency>
<!--注意这里要手动加上jboss-marshalling-serial.jar,否则会出现运行的时候服务端是接收不到数据的-->
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>1.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling-serial</artifactId>
<version>1.4.0.Final</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
NettyServer
import com.yjrj.presalehouse.feign.UserClient;
import com.yjrj.presalehouse.handler.RequestServerHandler;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
/**
* 接收消息
* @version V1.0
* @file: com.yjrj.presalehouse.server.NettyServer
* @description:
* @author: wanggd
* @date 2021-04-06
*/
@Component
@Slf4j
public class NettyServer {
/**
* boss 线程组用于处理连接工作
*/
private EventLoopGroup boss = new NioEventLoopGroup();
/**
* work 线程组用于数据处理
*/
private EventLoopGroup work = new NioEventLoopGroup();
//6668
@Value("${netty.port:6668}")
private Integer port;
private ChannelFuture future;
@Autowired
private UserClient userClient;
/**
* 启动Netty Server
* @throws InterruptedException
*/
@PostConstruct
public void start() throws InterruptedException {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work)
// 指定Channel
.channel(NioServerSocketChannel.class)
//使用指定的端口设置套接字地址
.localAddress(new InetSocketAddress(port))
//服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
.option(ChannelOption.SO_BACKLOG, 1024)
//设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
// .childOption(ChannelOption.SO_KEEPALIVE, true)
//将小的数据包包装成更大的帧进行传送,提高网络的负载,即TCP延迟传输
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
socketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
socketChannel.pipeline().addLast(new RequestServerHandler(userClient));
}
});
future = bootstrap.bind().sync();
if (future.isSuccess()) {
log.info("启动 Netty Server");
}
}
@PreDestroy
public void destroy() throws InterruptedException {
log.info(" netty服务监听关闭: " + future.channel().localAddress());
try {
future.channel().closeFuture().sync();// 关闭服务器通道
} catch (Exception e) {
e.printStackTrace();
}finally{
boss.shutdownGracefully().sync();
work.shutdownGracefully().sync();
log.info("关闭Netty");
}
}
}
自定义业务处理handler
import com.yjrj.presalehouse.feign.UserClient;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义业务处理handler
* @version V1.0
* @file: com.yjrj.presalehouse.handler.RequestServerHandler
* @description:
* @author: wanggd
* @date 2021-04-06
*/
@Slf4j
public class RequestServerHandler extends ChannelInboundHandlerAdapter {
private UserClient userClient;
public RequestServerHandler(UserClient userClient){
this.userClient = userClient;
}
public RequestServerHandler(){
}
/**
* 服务端连接到客户端戳发该函数调用
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端连接到客户度:"+ctx.channel().remoteAddress());
}
/**
* 接收服务端的数据 并处理完
* @param ctx
* @param obj
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
System.out.println(obj);
if(null!=obj){
log.info("server received::"+obj.toString());
String receivedMsg = obj.toString();
/**
* call remote interface by feign
*/
try{
//业务代码
}catch (Exception exception){
//异常处理代码
log.error("system_error::"+sendDataHeader+"5001|"+exception.getMessage());
}
}
}
/**
* 发生异常 打印信息 关闭所有链路
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
}