最近接手了一个对接项目,需要给PC客户端提供restful API 同时需要和第三方建立socket通信,就想到了用springboot + netty 构建长连接服务,
一下是相关代码,
public class commonTestApplication {
public static void runNettyServer()
{
//Thread thread
Thread thread = new Thread(new NettyServer());
thread.start();
try
{
thread.join();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args) {
SpringApplication.run(commonTestApplication.class, args);
try {
runNettyServer();
//Thread.sleep(1000);
//runNettyClient();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
启动springboot netty server
netty server 和 netty client 实现
netty server 实现
public class NettyServer implements Runnable{
@Override
public void run() {
EventLoopGroup pGroup = new NioEventLoopGroup(); //线程组:用来处理网络事件处理(接受客户端连接)
EventLoopGroup cGroup = new NioEventLoopGroup(); //线程组:用来进行网络通讯读写
try {
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class) //注册服务端channel
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
//5s没有交互,就会关闭channel
sc.pipeline().addLast(new ServerHandlerOne()); //服务端业务处理类
}
});
ChannelFuture cf = null;
cf = b.bind(8765).sync();
cf.channel().closeFuture().sync();
}
catch(Exception e)
{
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
e.printStackTrace();
}
}
}
netty client 处理类
public class NettyClient {
private static class SingletonHolder {
static final NettyClient instance = new NettyClient();
}
public static NettyClient getInstance(){
return SingletonHolder.instance;
}
private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf ;
private NettyClient(){
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ClientHandlerOne()); //客户端业务处理类
}
});
}
public void connect(){
try {
this.cf = b.connect("127.0.0.1", 8765).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getChannelFuture(){
//如果管道没有被开启或者被关闭了,那么重连
if(this.cf == null){
this.connect();
}
if(!this.cf.channel().isActive()){
this.connect();
}
return this.cf;
}
}
server 端消息处理类
public class ServerHandlerOne extends ChannelHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active... ");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Server 接收到 :" + body );
String response = "返回给客户端的响应:" + body ;
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
//处理完毕,关闭服务端
//ctx.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("server: 读完了");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
client 端消息处理类
public class ClientHandlerOne extends ChannelHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Client 接收到server :" + body );
} finally {
// 记得释放xxxHandler里面的方法的msg参数: 写(write)数据, msg引用将被自动释放不用手动处理; 但只读数据时,!必须手动释放引用数
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("client: 读完了");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
测试
RestController
@CrossOrigin
@RequestMapping("controllerone/")
@Api(value = "controllerone", description = "测试")
public class controllerone {
public void runNettyClient()
{
try{
final NettyClient c = NettyClient.getInstance();
ChannelFuture cf = c.getChannelFuture();
cf.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
cf.channel().writeAndFlush(Unpooled.copiedBuffer("666".getBytes()));
cf.channel().writeAndFlush(Unpooled.copiedBuffer("888".getBytes()));
cf.channel().closeFuture().sync();
}
catch(Exception e)
{
e.printStackTrace();
}
}
@ApiOperation("用户登录")
@GetMapping("/login")
public Object login() {
try {
JSONObject ret = new JSONObject();
runNettyClient();
ret.put("hh","");
return ret;
} catch (Exception e) {
e.printStackTrace();
}
return new JSONObject();
}
}
远程服务器已经连接, 可以进行数据交换…
server channel active…
Server 接收到 :777666888
server: 读完了
Client 接收到server :返回给客户端的响应:777666888
client: 读完了
server: 读完了
测试是restful 接口调用nettyclient发送消息,客户端接收消息处理