Netty实现服务器

Netty是一个java的网络应用程序框架和工具。我们做Java web开发过程中,正常的应用使用SpringMVC/Jessey做rest接口,方便前端调用。而有时候,我们又会遇到IM即时通讯这样的需求,像Rest或者Webservice可能就不太适合做服务器端的实现。这时候可以采用Netty框架,方便基于TCP,UDP的服务开发。

下面我们看第一个例子,如何构建一个Netty的服务,需要些4个类。

1:NettyServer

package com.netty.start;

import io.netty.bootstrap.ServerBootstrap;
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.nio.NioServerSocketChannel;


/**
 * 服务器类.
 */
public class NettyServer {

    public void start(int port) throws Exception {

        //创建接收者的事件循环组
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        //创建访问者的事件循环组
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //创建服务器引导程序
            ServerBootstrap b = new ServerBootstrap();
            //设置消息循环
            b.group(parentGroup, childGroup);
            //设置通道
            b.channel(NioServerSocketChannel.class);
            //配置通道参数:连接队列的连接数
            b.option(ChannelOption.SO_BACKLOG, 1024);
            //设置客户端请求的处理操作
            b.childHandler(new ChildChannelHandler());
            //绑定端口,并获取通道io操作的结果
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            //关闭接收器事件循环
            parentGroup.shutdownGracefully();
            //关闭访问者的事件循环
            childGroup.shutdownGracefully();
        }

    }

}


2:ChildChannelHandler

package com.netty.start;

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.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;

/**
 * 客户端通道处理类.
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    static private Logger logger = LoggerFactory.getLogger(ChildChannelHandler.class);

    @Override
    protected void initChannel(SocketChannel e) throws Exception {

        logger.info(e.remoteAddress() + ":进入通道");
        ChannelPipeline pipeline = e.pipeline();
        // 以("\n")为结尾分割的 解码器
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
        // 字符串解码 和 编码
        pipeline.addLast("decoder", new StringDecoder(Charset.forName("GBK")));
        pipeline.addLast("encoder", new StringEncoder(Charset.forName("UTF-8")));
        //添加消息处理
        e.pipeline().addLast(new NettyServerHandler());

    }

}


3:NettyServerHandler

package com.netty.start;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;

/**
 * 服务器处理类.
 */
public class NettyServerHandler extends ChannelHandlerAdapter {

    static private Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    /**
     * 连接通道.
     *
     * @param ctx
     * @param remoteAddress
     * @param localAddress
     * @param promise
     * @throws Exception
     */
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":连接通道");
        super.connect(ctx, remoteAddress, localAddress, promise);
    }

    /**
     * 活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道激活");
        super.channelActive(ctx);
        ctx.writeAndFlush("欢迎访问服务器!!!\r\n");
    }

    /**
     * 非活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道失效");
        super.channelInactive(ctx);
    }

    /**
     * 接收消息.
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":" + msg);
    }

    /**
     * 接收完毕.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    /**
     * 关闭通道.
     *
     * @param ctx
     * @param promise
     * @throws Exception
     */
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":关闭通道");
        super.close(ctx, promise);
    }

    /**
     * 异常处理.
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("异常信息:" + cause.getMessage());
        super.exceptionCaught(ctx, cause);
    }
}

4:NettyServerTest

package com.netty.start;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettyServerTest {

    static private Logger logger = LoggerFactory.getLogger(NettyServerTest.class);


    @Test
    public void test() {
        try {
            logger.info("启动netty服务端");
            new NettyServer().start(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


启动NettyServerTest进行单元测试。

在cmd中输入:

telnet 127.0.0.1 3000

然后输入文本内容,服务器控制台会输出请求内容。

注意:如果ChildChannelHandler初始化的时候不添加编码和解码器,那么NettyServerHandler在接收内容的时候需要将数据转成byte,再转String。所以,配置好比较方便。


转载于:https://my.oschina.net/chenhao901007/blog/639045

下面是一个使用Spring Boot和Netty实现服务器之间推送消息的简单示例代码: 首先,需要引入Netty和Spring Boot的依赖: ```xml <!-- Netty --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.42.Final</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 接下来,定义一个WebSocket服务器处理程序: ```java import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel incoming = ctx.channel(); channels.add(incoming); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel incoming = ctx.channel(); channels.remove(incoming); } @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // 收到消息后,将消息转发给所有连接的客户端 for (Channel channel : channels) { if (channel != ctx.channel()) { channel.writeAndFlush(new TextWebSocketFrame(msg.text())); } } } } ``` 然后,在Spring Boot应用程序中定义WebSocket服务器: ```java import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketServerConfig { @Autowired private WebSocketServerHandler webSocketServerHandler; @Autowired private ServerEndpointExporter serverEndpointExporter; public void init() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(webSocketServerHandler); } }); ChannelFuture future = bootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 最后,在Spring Boot应用程序的启动类中调用WebSocket服务器初始化方法: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class MyApplication { @Bean public WebSocketServerHandler webSocketServerHandler() { return new WebSocketServerHandler(); } @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); WebSocketServerConfig webSocketServer = new WebSocketServerConfig(); try { webSocketServer.init(); } catch (Exception e) { e.printStackTrace(); } } } ``` 这样,就可以使用Netty和Spring Boot实现服务器之间的消息推送了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值