原創:轉載需註明原創地址 https://www.cnblogs.com/fanerwei222/p/11827026.html
本文介紹Netty的使用, 結合我本人的一些理解和操作來快速的讓初學者入門Netty, 理論知識會有, 但是不會太深入, 夠用即可, 僅供入門! 需要想詳細的知識可以移步Netty官網查看官方文檔!
理論知識 : Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序
當然, 我們這裏主要是用Netty來發送消息, 接收消息, 測試一下demo, 更厲害的功能後面再慢慢發掘, 我們先看看這玩意怎麼玩, 後面再深入
需要工具和Java類:
netty-4.1.43
netty服務器類 SayHelloServer.java
netty服務端處理器類 SayHelloServerHandler.java
netty客戶端類 SayHelloClient.java
netty客戶端處理器類 SayHelloClientHandler.java
服務器main方法測試類 MainNettyServer.java
客戶端main方法測試類 MainNettyClient.java
首先先來一張演示圖, 最下面也會放:
我們看完以下部分就能實現這個東西了!
話不多說, 先貼代碼:
package netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import netty.handler.SayHelloServerHandler;
/**
* sayhello 服務器
*/
public class SayHelloServer {
/**
* 端口
*/
private int port ;
public SayHelloServer(int port){
this.port = port;
}
public void run() throws Exception{
/**
* Netty 負責裝領導的事件處理線程池
*/
EventLoopGroup leader = new NioEventLoopGroup();
/**
* Netty 負責裝碼農的事件處理線程池
*/
EventLoopGroup coder = new NioEventLoopGroup();
try {
/**
* 服務端啓動引導器
*/
ServerBootstrap server = new ServerBootstrap();
server
.group(leader, coder)//把事件處理線程池添加進啓動引導器
.channel(NioServerSocketChannel.class)//設置通道的建立方式,這裏採用Nio的通道方式來建立請求連接
.childHandler(new ChannelInitializer<SocketChannel>() {
//構造一個由通道處理器構成的通道管道流水線
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 此處添加服務端的通道處理器
*/
socketChannel.pipeline().addLast(new SayHelloServerHandler());
}
})
/**
* 用來配置一些channel的參數,配置的參數會被ChannelConfig使用
* BACKLOG用於構造服務端套接字ServerSocket對象,
* 標識當服務器請求處理線程全滿時,
* 用於臨時存放已完成三次握手的請求的隊列的最大長度。
* 如果未設置或所設置的值小於1,Java將使用默認值50
*/
.option(ChannelOption.SO_BACKLOG, 128)
/**
* 是否啓用心跳保活機制。在雙方TCP套接字建立連接後(即都進入ESTABLISHED狀態)
* 並且在兩個小時左右上層沒有任何數據傳輸的情況下,這套機制纔會被激活。
*/
.childOption(ChannelOption.SO_KEEPALIVE, true);
/**
* 服務端綁定端口並且開始接收進來的連接請求
*/
ChannelFuture channelFuture = server.bind(port).sync();
/**
* 查看一下操作是不是成功結束了
*/
if (channelFuture.isSuccess()){
//如果沒有成功結束就處理一些事情,結束了就執行關閉服務端等操作
System.out.println("服務端啓動成功!");
}
/**
* 關閉服務端
*/
channelFuture.channel().closeFuture().sync();
System.out.println("服務端即將關閉!");
} finally {
/**
* 關閉事件處理組
*/
leader.shutdownGracefully();
coder.shutdownGracefully();
System.out.println("服務端已關閉!");
}
}
}
package netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 服務端入站處理器適配器的繼承類
* 用來處理服務端的一些事情
* 根據需要來實現一些方法
*/
public class SayHelloServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("直接打印服務端需要處理的信息: " + buf.toString(CharsetUtil.UTF_8));
ByteBuf res = Unpooled.wrappedBuffer(new String("塔臺收到!塔臺收到!信息如下, 請確認 " + buf.toString(CharsetUtil.UTF_8)).getBytes());
/**
* 給客戶端回覆消息
*/
ctx.writeAndFlush(res);
}
/**
* 連接成功後,自動執行該方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("服務器首次處理!");
/**
* 這種發送的消息格式是錯誤的!!!!!
* 消息格式必須是ByteBuf纔行!!!!!
*/
ctx.writeAndFlush("Hello is server !");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
/**
* 異常捕獲
*/
cause.printStackTrace();
ctx.close();
}
}
package netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import netty.handler.SayHelloClientHandler;
/**
* sayhello 客戶端
*/
public class SayHelloClient {
private int port;
private String host = "127.0.0.1";
private Channel channel;
public SayHelloClient(int port){
this.port = port;
}
/**
* 客戶端運行方法
* @throws InterruptedException
*/
public void run() throws InterruptedException {
/**
* 負責裝客戶端的事件處理線程池
*/
EventLoopGroup clientWorker = new NioEventLoopGroup();
try {
/**
* netty客戶端引導啓動器
*/
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(clientWorker)//把事件處理線程池添加進啓動引導器
.channel(NioSocketChannel.class)//設置通道的建立方式,這裏採用Nio的通道方式來建立請求連接
//.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 此處添加客戶端的通道處理器
*/
socketChannel.pipeline().addLast(new SayHelloClientHandler());
}
});
/**
* 客戶端綁定端口並且開始發起連接請求
*/
ChannelFuture future = bootstrap.connect(host, port).sync();
if (future.isSuccess()){
System.out.println("客戶端連接服務器成功!");
}
/**
* 將通道設置好, 以便外面獲取
*/
this.channel = future.channel();
/**
* 關閉客戶端
*/
future.channel().closeFuture().sync();
System.out.println("客戶端即將關閉!");
} finally {
/**
* 關閉事件處理組
*/
clientWorker.shutdownGracefully();
System.out.println("客戶端已關閉!");
}
}
public Channel getChannel(){
return this.channel;
}
}
package netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.nio.charset.Charset;
import java.util.Date;
/**
* sayhello 客戶端處理器
*/
public class SayHelloClientHandler extends ChannelInboundHandlerAdapter {
/**
* 通道信息讀取處理
* @param ctx
* @param msg
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg; // 將消息轉化成bytebuf
try {
System.out.println("客戶端直接打印接收到的消息: " + m.toString(Charset.defaultCharset()));
long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
/**
* 給服務端回覆消息
*/
ctx.writeAndFlush("客戶端收到! 消息爲: " + m.toString(Charset.defaultCharset()));
} finally {
m.release();
}
}
/**
* 連接成功後,自動執行該方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
/**
* 往服務端發送消息
* 消息格式必須是ByteBuf纔行!!!!!
* 如果是其他格式服務端是接收不到的!!!!
*/
String helo = "你好呀!";
ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
ctx.channel().writeAndFlush(byteBuf);
System.out.println("首次連接完成!");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
package netty;
import netty.server.SayHelloServer;
/**
* Netty server 使用main類
*/
public class MainNettyServer {
/**
* 端口
*/
private static int port = 8686;
public static void main(String[] args) throws Exception {
/**
* 啓動netty服務器
*/
SayHelloServer sayHelloServer = new SayHelloServer(port);
sayHelloServer.run();
}
}
package netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import netty.client.SayHelloClient;
import java.util.Scanner;
/**
* 客戶端main方法類
*/
public class MainNettyClient {
public static void main(String[] args) throws InterruptedException {
/**
* 創建netty客戶端
*/
SayHelloClient client = new SayHelloClient(8686);
/**
* 新建一個線程讓它單獨去跑,我們可以main方法測試一下發送消息和接受消息
*/
Thread clientThread = new Thread(new Runnable() {
@Override
public void run() {
try {
client.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
clientThread.start();
/**
* 如果不新建一個線程去跑客戶端的話, 以下的代碼就執行不到
* 這裏用while是因爲客戶端的channel並不能立馬生成, 會在client啓動後一段時間才生成獲取到
* 所以需要延遲一點獲取channel, 否則channel爲null
*/
Channel channel = null;
boolean isStart = false;
while (!isStart) {
if (null != client.getChannel()) {
channel = client.getChannel();
isStart = true;
}
}
String helo = "你好呀!我這裏是客戶端, 收到請回答";
ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
channel.writeAndFlush(byteBuf);
/**
* 我們通過控制檯輸入來給服務端發送消息
* 此處只做模擬使用
*/
for (int i = 0; i < 10 ; i++) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
channel.writeAndFlush(Unpooled.wrappedBuffer(text.getBytes()));
}
}
}