二、Netty入门到精通(RPC实现)——Netty服务端helloWorld入门
目录一、Netty介绍1、基本介绍2、主要应用二、Netty服务端hello world1、导入 pom2、server 服务端代码3、测试4、分析一、Netty介绍1、基本介绍NIO是一个非阻塞的多线程的socket网络通信API每次使用NIO进行网络通信的时候我们都需要自己编写网络交互的服务端、客户端,都要去编写数据的接收、解析...
目录
一、Netty介绍
1、基本介绍
-
NIO是一个非阻塞的多线程的socket网络通信API
-
每次使用NIO进行网络通信的时候我们都需要自己编写网络交互的服务端、客户端,都要去编写数据的接收、解析、返回等逻辑方法,十分麻烦。基于此,JBOSS就推出了一个可以快速开发高性能、高可靠性的网络服务器和客户端程序的框架-----Netty。
-
Netty说白了就是一个基于NIO的客户、服务器端编程框架,本来需要写很久的客户端和服务端的代码,这里只需要Netty就可以快速、简单的开发出一个实现某种协议的网络应用。
-
Netty版本大致版本分为Netty3.x、Netty4.x和Netty5.x。
2、主要应用
(1)分布式进程通信
像Hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于Netty实现的,这些框架版本通常都还在使用Netty3.x。
(2)游戏服务器开发
众所周知,许多网络游戏需要在各个客户端传输网络信息,所以也需要快速搭建客户端与服务端之间的网络交互逻辑。
最新的游戏服务器有部分公司可能已经开始使用Netty4.x和Netty5.x。
二、Netty服务端hello world
主要针对Netty3.x进行讲解,后面逐步衍生到Netty4.x以上
1、导入 pom
<properties>
<!--<netty.version>4.1.41.Final</netty.version>-->
<netty.version>3.10.5.Final</netty.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
2、server 服务端代码
Server.java
import org.jboss.netty.channel.Channels;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* \* Created with IntelliJ IDEA.
* \* User: sunxianpeng
* \* Date: 2019/9/29
* \* Time: 18:32
* \* To change this template use File | Settings | File Templates.
* \* Description:based on netty 3.10.5.Final
* \
*/
public class Server {
public static void main(String[] args) {
//1.创建一个Netty服务类
/**
* ServerBootstrap 是一个启动NIO服务的辅助启动类
* 你可以在这个服务中直接使用Channel
* */
ServerBootstrap serverBootstrap = new ServerBootstrap();
//2.创建两个线程池
/**
* 第一个经常被叫做‘boss’,用来接收进来的连接。
* 第二个经常被叫做‘worker’,用来处理已经被接收的连接,
* 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。
* */
ExecutorService boss = Executors.newCachedThreadPool();
ExecutorService worker = Executors.newCachedThreadPool();
//3.为服务类设置一个NioSocket工厂
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));
//4.设置管道的工厂(匿名内部类实现)
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//设置一个处理服务端消息和各种消息事件的类(Handler)
pipeline.addLast("hellohandler", new HelloHandler());
return pipeline;
}
});
//5.为服务端设置一个端口
serverBootstrap.bind(new InetSocketAddress(10101));
System.out.println("server start!");
}
}
HelloHandler.java
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* \* Created with IntelliJ IDEA.
* \* User: sunxianpeng
* \* Date: 2019/9/29
* \* Time: 18:43
* \* To change this template use File | Settings | File Templates.
* \* Description:based on netty 3.10.5.Final
* \
*/
public class HelloHandler extends SimpleChannelHandler {
/**
* 连接关闭
*/
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
System.out.println("channelClosed");
super.channelClosed(ctx, e);
}
/**
* 新建连接
* */
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
System.out.println("channelConnected");
super.channelConnected(ctx, e);
}
/**
* 连接断开
* */
@Override
public void channelDisconnected(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
System.out.println("channelDisconnected");
super.channelDisconnected(ctx, e);
}
/**
* 捕获异常
* */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
System.out.println("exceptionCaught,error:"+e.toString());
super.exceptionCaught(ctx, e);
}
/**
* 消息接收
* */
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println("messageReceived");
//获取到的信息是使用ChannelBuffer包装的字节流
ChannelBuffer message = (ChannelBuffer)e.getMessage();
String msg = new String(message.array());//将字节流转换为String
System.out.println(msg);//打印收到的字符串
super.messageReceived(ctx, e);
}
}
3、测试
启动服务端,在window 控制台 使用 telnet 开启多个客户端 连接服务端
telnet 127.0.0.1 10101
按键 Ctrl + ] 后,使用如下方法,向服务端发送信息
send hello world
可以同时处理过个连接。
4、分析
(1)关闭cmd,可以看到控制台先打印了“channelDisconnected”,然后打印了“channelClosed”
原因是,对于channelDisconnected方法,是必须有连接建立之后,关闭通道的时候才会触发此方法,而对于channelClosed就是无论连接建立与否,在channel关闭的时候就会触发。(例如客户端连接失败后,不会触发channelDisconnected,只会触发channelClosed)。
(2)我们的消息接收时总是要把字节流转换为String字符串,可不可以省去这一步,直接拿到字符串呢?
是可以的,我们只需要在服务端的管道的工厂中设置decoder解码类为“StringDecoder”类即可:
//4.设置管道的工厂(匿名内部类实现)
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//设置解码方式为String
pipeline.addLast("decoder", new StringDecoder());
//设置一个处理服务端消息和各种消息事件的类(Handler)
pipeline.addLast("hellohandler", new HelloHandler());
return pipeline;
}
});
/**
* 消息接收
* */
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println("messageReceived");
String msg = (String)e.getMessage();//将字节流转换为String
System.out.println(msg);//打印收到的字符串
super.messageReceived(ctx, e);
}
(3)Server服务端接收到信息后,可以回写给客户端信息,我们在messageReceived中编写回复逻辑
/**
* 消息接收
* */
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
System.out.println("messageReceived");
String msg = (String)e.getMessage();//将字节流转换为String
System.out.println(msg);//打印收到的字符串
//回写数据(需要用ChannelBuffer包装要回复的字节流信息)
ChannelBuffer channelBuffer = ChannelBuffers.copiedBuffer("hi".getBytes());
ctx.getChannel().write(channelBuffer);
super.messageReceived(ctx, e);
}
重新启动服务端,然后使用telnet进去后(不用ctrl+])随便输一个字符(这里输入a),就拿到了服务端的回复信息
更多推荐
所有评论(0)