一,准备工作

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. CPP-基础:关于内存分配

    1:c中的malloc和c++中的new有什么区别 (1)new.delete 是操作符,可以重载,只能在C++中使用.(2)malloc.free是函数,可以覆盖,C.C++中都可以使用.(3)ne ...

  2. CS193p Lecture 8 - Protocols, Blocks and Animation

    一.协议(Protocols) 1. 声明协议 @protocol Foo <Xyzzy, NSObject> // ... @optinal // @required //... @en ...

  3. vue input 判断

    //输入框 判断 //全局异常提示信息 //b 1:失去焦点验证错误提示 2:得到焦点关闭错误提示 //i 来区分是验证那个input框 check:function (t,b) { var that ...

  4. 【Git版本控制】git---从已有分支拉出新的分支

    参考博文:git---从已有分支拉出新分支

  5. 利用Resttemplate进行put请求

    开发中,最常用的是post.get这两种.今天我给大家展示一个利用put请求的demo,其实put请求跟post请求没啥区别,但是没有返回值. void put(String var1, @Nulla ...

  6. Python面向对象(类之间的关系)(三)

    类与类之间的关系 在我们的世界中事物和事物之间总会有一些联系. 在面向对象中. 类和类之间也可以产生相关的关系 1. 依赖关系 执行某个动作的时候. 需要xxx来帮助你完成这个操作. 此时的关系是最轻 ...

  7. LeetCode 673. Number of Longest Increasing Subsequence

    Given an unsorted array of integers, find the number of longest increasing subsequence. Example 1: I ...

  8. 几条sql语句(exists)

    通常exists后的子查询是需要和外面的表建立关联关系的,如 select count(*) from a where exists (select 'x' from b where a.id = b ...

  9. pep-8要求归纳

    代码布局缩进每个缩进级别使用4个空格. 连续行应使用Python的隐式行连接括号,括号和大括号,或使用悬挂缩进 来垂直对齐包装元素.当使用悬挂式缩进时,应考虑以下内容:第一行应该没有任何争论,应该使用 ...

  10. javascript前端下载

    <html> <head> <title>测试标题</title> </head> <body> <div> 测试页 ...