Netty构建游戏服务器(三)--netty spring简单整合

一,基本方法

上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务。

在netty服务器中,我们建立了三个类:HelloServer(程序主入口) , HelloServerInitializer(传输通道初始化),HelloServerHandler(业务控制器)

这三个类中HelloServer中new了一个HelloServerInitializer,在HelloServerInitializer最后又new了一个HelloServerHandler。其中需要new的地方,就是spring要管理的地方。

二,实现源码

1,相关准备

导入需要的JAR包和相关依赖,主要是spring,log4j,netty等,后面的项目源码中包含了所有jar包;

另外你可以使用maven来构建项目,添加各种jar包。

2,主程序TestSrping.java

主程序很简单,就是加载spring配置文件就可以了。

package com.wayhb;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {

    private static Logger log = Logger.getLogger(TestSpring.class); 

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //加载spirng配置文件
        ApplicationContext context= new ClassPathXmlApplicationContext("server.xml");

    }

}

3,spring的XML配置单server.xml和LOG4J配置文件

server.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:lang="http://www.springframework.org/schema/lang" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
    default-lazy-init="false" default-autowire="byName">

<!--  <bean id="user" class="com.wayhb.User">
<property name="userid" value="20"></property>
<property name="username" value="wayhb"></property>
</bean>-->
<!--开启注解方式,扫描com.wayhb,com.netty两个包路径-->
<context:annotation-config/>
<context:component-scan base-package="com.wayhb,com.netty" /> 

<!-- 传统方法配置BEAN
<bean id="helloServer" class="com.netty.HelloServer" init-method="serverStart">
<property name="helloServerInitializer" ref="helloServerInitializer"></property>
</bean>

<bean id="helloServerInitializer" class="com.netty.HelloServerInitializer">
<property name="helloServerHandler" ref="helloServerHandler"></property>
</bean>

<bean id="helloServerHandler" class="com.netty.HelloServerHandler" scope="prototype"></bean>
 -->

</beans>

log4j.properties

### 设置###
#log4j.rootLogger = debug,stdout,D,E
log4j.rootLogger = debug,stdout
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = C://logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File =C://logs/error.log
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

其中输出功能是关闭的,需要的话去掉#就可以了

4,HelloServer

package com.netty;

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;

import javax.annotation.PostConstruct;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//注解方式注入bean,名字是helloServer
@Service("helloServer")
public class HelloServer {
    private static Logger log = Logger.getLogger(HelloServer.class);
    /**
     * 服务端监听的端口地址
     */
    private static final int portNumber = 7878;

    //自动装备变量,spring会根据名字或者类型来装备这个变量,注解方式不需要set get方法了
    @Autowired
    private HelloServerInitializer helloServerInitializer;

    //程序初始方法入口注解,提示spring这个程序先执行这里
    @PostConstruct
    public void serverStart() throws InterruptedException{
         EventLoopGroup bossGroup = new NioEventLoopGroup();
         EventLoopGroup workerGroup = new NioEventLoopGroup();
         try {
             ServerBootstrap b = new ServerBootstrap();
             b.group(bossGroup, workerGroup);
             b.channel(NioServerSocketChannel.class);
             b.childHandler(helloServerInitializer);

             // 服务器绑定端口监听
             ChannelFuture f = b.bind(portNumber).sync();
             // 监听服务器关闭监听
             f.channel().closeFuture().sync();

             log.info("###########################################");
             // 可以简写为
             /* b.bind(portNumber).sync().channel().closeFuture().sync(); */
         } finally {
             bossGroup.shutdownGracefully();
             workerGroup.shutdownGracefully();
         }
    }

}

注解方式更加简便,配置内容少了很多

5,HelloServerInitializer

package com.netty;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

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;

@Service("helloServerInitializer")
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {

    @Autowired
    private HelloServerHandler helloServerHandler;

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 以("\n")为结尾分割的 解码器
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));

        // 字符串解码 和 编码
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());

        // 自己的逻辑Handler
        pipeline.addLast("handler", helloServerHandler);
    }
}

6,HelloServerHandler

package com.netty;

import java.net.InetAddress;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.channel.ChannelHandler.Sharable;

@Service("helloServerHandler")
@Scope("prototype")
//特别注意这个注解@Sharable,默认的4版本不能自动导入匹配的包,需要手动加入
//地址是import io.netty.channel.ChannelHandler.Sharable;
@Sharable
public class HelloServerHandler extends SimpleChannelInboundHandler<String> {

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {  // (2)
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
        }
        channels.add(ctx.channel());
    } 

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
        }
        channels.remove(ctx.channel());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        // 收到消息直接打印输出
        System.out.println(ctx.channel().remoteAddress() + " Say : " + msg);

        // 返回客户端消息 - 我已经接收到了你的消息
        ctx.writeAndFlush("Received your message !\n");
    }

    /*
     *
     * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候)
     *
     * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述
     * */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");

        ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n");

        super.channelActive(ctx);
    }
}

特别注意这个注解@Sharable,默认的4版本不能自动导入匹配的包,需要手动加入
地址是import io.netty.channel.ChannelHandler.Sharable;

三,运行测试

1,打开TestSpring.java,右键运行,正常情况下服务器会开启;

2,打开上节写的项目,打开HelloClient.java,右键运行;

3,HelloClient.java上再运行一次,出现两个客户端,如果都连接成功,说明项目没有问题。

注意事项:netty4需要@Sharable才可以开启多个handler,所以需要多个连接的handler类上要加入注解@Sharable,该注解自动导入有问题,手动加入包import io.netty.channel.ChannelHandler.Sharable;

原文地址:https://www.cnblogs.com/WangBoBlog/p/8718269.html

时间: 2024-08-30 09:02:21

Netty构建游戏服务器(三)--netty spring简单整合的相关文章

Netty构建游戏服务器(二)--Hello World

一,准备工作 1,netty-all-4.1.5.Final.jar(官网下载) 2,eclipse 二,步骤概要 1,服务器开发 (1),创建Server类 该类是程序的主入口,有main方法,服务器开启也是在此执行. 该类主要是提供了channel链接,绑定了端口. 该类需要new一个Initalizer类来完成服务器开启. (2),创建Initalizer类 该类是初始化类,主要是创建了传输通道ChannelPipeline,然后在通道中加入了一些列的Handler,其中解码和编码Hand

Netty构建Http服务器

Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议.Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发.Netty 已逐渐成为 Java NIO 编程的首选框架,Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,因此我们在构建Http服务器的时候就是通过Htt

游戏服务器三问

①游戏服务器与传统互联网的服务器比起来是更复杂还是更简单? ②游戏服务器对于   响应时间和吞吐量 是否关心? ③游戏服务器是否需要支持事务?是否需需要使用数据网格?

struts2+hibernate+spring简单整合且java.sql.SQLException: No suitable driver 问题解决

最近上j2ee的课,老师要求整合struts2+hibernate+spring,我自己其实早早地有准备弄的,现在都第9个项目了,无奈自己的思路和头绪把自己带坑了,当然也是经验问题,其实只是用myeclipse进行整合的,本来也没那么多问题,看视频吧居然好多要手打,我不喜欢看不下去放弃了,教程把就是一堆坑,最最让人不解的是明明有一个冲突是需要解决的,但我看到的教程居然都没有提到,还有一个错误居然好多人都好像自动忽略一样,能解决我问题的都是要漫长的找,所以我一定一定要把这个过程记录下来,给第一次搞

上手spring boot项目(三)之spring boot整合mybatis进行增删改查的三种方式。

1.引入依赖 <!--springboot的web起步依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency> <!-- Mybatis起步依赖 --> <dependency> <groupId>o

基于Netty打造RPC服务器设计经验谈

自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热情的反馈和若干个优化建议,于是利用闲暇时间,打算对原来NettyRPC中不合理的模块进行重构,并且增强了一些特性,主要的优化点如下: 在原来编码解码器:JDK原生的对象序列化方式.kryo.hessian,新增了:protostuff. 优化了NettyRPC服务端的线程池模型,支持LinkedBl

erlang 游戏服务器开发

http://blog.csdn.net/slmeng2002/article/details/5532771 最近关注erlang游戏服务器开发  erlang大牛写的游戏服务器值得参考 介绍本文以我的OpenPoker项目为例子,讲述了一个构建超强伸缩性的在线多游戏玩家系统.OpenPoker是一个超强多玩家纸牌服务器,具有容错.负载均衡和无限伸缩性等特性.源代码位于我的个人站点上,大概10,000行代码,其中1/3是测试代码. 在OpenPoker最终版本敲定之前我做了大量调研,我尝试了D

Spring Boot 整合JDBC 实现后端项目开发

一.前言 前后端分离开发是将项目开发工作前后端交互工作拆分,使前端开发人员能够专注于页面开发或APP 开发,后端开发人员专注与接口开发.业务逻辑开发. 此处开发后端项目,给前端或者APP 端提供接口.不涉及复杂的业务逻辑,通过简单的增删改查来演示后端开发项目. 环境介绍: 开发工具:IDEA JDK: 1.7 及以上 Spring Boot: 2.0 及以上 Maven: 3.0 及以上 二.新建Spring Boot 项目 通过功能菜单File - New - Project 新建Spring

Redis的安装、部署和与Spring Cache整合

安装 去http://redis.io/下载最新稳定版的源码.解压,进入解压目录,执行 make make install 之后在src/目录下会多出以下几个文件: redis-server redis-benchmark redis-cli redis-conf 将其copy至/usr/redis目录下即可. 部署 修改redis-conf文件,添加: requirepass 111111 将连接密码设为111111.然后执行 ./redis-server redis-conf 即可启动red