一,准备工作

1,netty-all-4.1.5.Final.jar(官网下载)

2,eclipse

二,步骤概要

1,服务器开发

(1),创建Server类

该类是程序的主入口,有main方法,服务器开启也是在此执行。

该类主要是提供了channel链接,绑定了端口。

该类需要new一个Initalizer类来完成服务器开启。

(2),创建Initalizer类

该类是初始化类,主要是创建了传输通道ChannelPipeline,然后在通道中加入了一些列的Handler,其中解码和编码Handler是Netty自带的辅助类,在最后假如了自定义业务控制器类。

(3),创建Handler类

该类是自定义的业务控制器,需要的逻辑都在这里实现,例如收到信息如何处理,断开连接如何发送消息等。

2,客户端开发

和服务器端开发类似,不同在于:

(1),client类(对应服务器的server类)只需要一个EventLoopGroup,而服务器类需要两个。

(2),编码解码器要和服务器一致。

三,具体实现

1,项目树形图

服务器端和客户端分别有三个类,HelloClient是客户端主入口,HelloServer是服务器端主入口。

2,服务器代码

(1),HelloServer

package org.example.hello;

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 HelloServer { /**
* 服务端监听的端口地址
*/
private static final int portNumber = 7878; public static void main(String[] args) throws InterruptedException {
//开启两个事件循环组,事件循环组会自动构建EventLoop,服务器一般开启两个,提高效率
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//Netty的引导类,用于简化开发
ServerBootstrap b = new ServerBootstrap();
//把事件循环组加入引导程序
b.group(bossGroup, workerGroup);
//开启socket
b.channel(NioServerSocketChannel.class);
//加入业务控制器,这里是加入一个初始化类,其中包含了很多业务控制器
b.childHandler(new HelloServerInitializer()); // 服务器绑定端口监听
ChannelFuture f = b.bind(portNumber).sync();
// 监听服务器关闭监听
f.channel().closeFuture().sync(); // 可以简写为
/* b.bind(portNumber).sync().channel().closeFuture().sync(); */
} finally {
//Netty优雅退出
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

(1),HelloServerInitializer

初始化传输通道

package org.example.hello;

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; //继承Netty提供的初始化类,只要复写其中的方法就可以了
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> { @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", new HelloServerHandler());
//初始化类一般都是先加入编码解码器来解读传输来的消息,然后加入自定义类来处理业务逻辑
}
}

(1),HelloServerHandler

定义业务逻辑

package org.example.hello;

import java.net.InetAddress;

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; //继承Netty提供的通道传入处理器类,只要复写方法就可以了,简化开发
public class HelloServerHandler extends SimpleChannelInboundHandler<String> { //获取现有通道,一个通道channel就是一个socket链接在这里
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());
} //消息读取有两个方法,channelRead和channelRead0,其中channelRead0可以读取泛型,常用
//收到消息打印出来,并返还客户端消息
@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);
}
}

3,客户端代码

(1),HelloClient

package org.example.hello;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader; public class HelloClient { public static String host = "127.0.0.1";
public static int port = 7878; /**
* @param args
* @throws InterruptedException
* @throws IOException
*/
public static void main(String[] args) throws InterruptedException, IOException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new HelloClientInitializer()); // 连接服务端
Channel ch = b.connect(host, port).sync().channel(); // 控制台输入
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//也可以用while循环
for (;;) {
String line = in.readLine();
if (line == null) {
continue;
}
/*
* 向服务端发送在控制台输入的文本 并用"\r\n"结尾
* 之所以用\r\n结尾 是因为我们在handler中添加了 DelimiterBasedFrameDecoder 帧解码。
* 这个解码器是一个根据\n符号位分隔符的解码器。所以每条消息的最后必须加上\n否则无法识别和解码
* */
ch.writeAndFlush(line + "\r\n");
}
} finally {
// The connection is closed automatically on shutdown.
group.shutdownGracefully();
}
}
}

(2),HelloClientInitializer

package org.example.hello;

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; public class HelloClientInitializer extends ChannelInitializer<SocketChannel> { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); /*
* 这个地方的 必须和服务端对应上。否则无法正常解码和编码
*
* 解码和编码 我将会在下一张为大家详细的讲解。再次暂时不做详细的描述
*
* */
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder()); // 客户端的逻辑
pipeline.addLast("handler", new HelloClientHandler());
}
}

(3),HellClientHandler

package org.example.hello;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; public class HelloClientHandler extends SimpleChannelInboundHandler<String> { @Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("Server say : " + msg);
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client active ");
super.channelActive(ctx);
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client close ");
super.channelInactive(ctx);
}
}

四,运行测试

服务器右键运行程序

客户端右键运行程序

服务器端console:

客户端console:

切换console:

Netty构建游戏服务器(二)--Hello World的更多相关文章

  1. Netty构建游戏服务器(一)--基本概念与原理

    一,Netty是什么 1,Netty是由 JBOSS提供的一个 java开源框架. 2,Netty是JAR包,一般使用ALL-IN-ONE的JAR包就可以开发了. 3,Netty不需要运行在Tomca ...

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

    一,基本方法 上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务. 在netty服务器中,我们建立了三个类:HelloServer(程序主入口) ...

  3. Netty构建Http服务器

    Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络 ...

  4. Netty游戏服务器二

    上节我们写个server主类,那么发现什么事情都干不了,是的,我们还没有做任何的业务处理. 接着我们开始写处理客户端连接,发送接收数据的类ServerHandler. public class Ser ...

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

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  6. Netty游戏服务器之一

    所谓磨刀不误砍柴工,所以在搭建netty游戏服务器之前,我们先要把要准备的东西做好. 首先进入netty的官网下载最新版本的netty的jar包,http://netty.io/downloads.h ...

  7. Java游戏服务器搭建

    一.前言 此游戏服务器架构是一个单服的形式,也就是说所有游戏逻辑在一个工程里,没有区分登陆服务器.战斗服务器.世界服务器等.此架构已成功应用在了多款页游服务器 .在此框架中没有实现相关业务逻辑,只有简 ...

  8. Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇

    目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...

  9. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

随机推荐

  1. 浏览器window产生的缓存九种解决办法

    浏览器缓存(Browser Caching)是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器 ...

  2. scanf函数详解

    函数名: scanf 功 能: 执行格式化输入 用 法: int scanf(char *format[,argument,...]);scanf()函数是通用终端格式化输入函数,它从标准输入设备(键 ...

  3. 清北学堂2018DP&图论精讲班 DP部分学习笔记

    Day 1 上午 讲的挺基础的--不过还是有些地方不太明白 例1 给定一个数n,求将n划分成若干个正整数的方案数. 例2 数字三角形 例7 最长不下降子序列 以上太过于基础,不做深入讨论 例3 给定一 ...

  4. [BZOJ] 2662: [BeiJing wc2012]冻结

    https://www.lydsy.com/JudgeOnline/problem.php?id=2662 第一次写分层图(捂脸) 一开始真的naive地建图了,T到飞起.. 可以省下建图的空间,直接 ...

  5. 八:SQL之DQL数据查询语言单表操作

    前言: DQL数据库查询语言是我们在开发中最常使用的SQL,这一章总结了单表操作部分的常用查询方式 主要操作有:查询所有字段.查询指定字段.查询指定记录.带IN的关键字查询,范围查询,陪查询.查询空值 ...

  6. ubuntu 安装 php7.2

    sudo apt-get install software-properties-common python-software-properties sudo add-apt-repository p ...

  7. 如何用DOM 元素就能画出国宝熊猫

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/odKrpy 可交互视频教 ...

  8. 计算机/ARM 系统

    这方面知识,自己有了大概了解,可以再没有重要急需学习的安排时,不时的翻翻这方面的书.本能力属于重要但不急需的能力

  9. js事件,操作页面文档,计算后样式,数据类型

    js:运行在浏览器的脚本语言 js引入 1.行间式:存在于行间事件中 <div id="div" onclick="this.style.color="r ...

  10. 前端,基础选择器,嵌套关系.display属性,盒模型

    基础选择器 1.统配选择器 控制html,body及body内跟显示相关的标签 *{ width:80px; height:80px; background-color:red; } 2.类选择器 以 ...