Netty是由JBOSS提供的一個java開源框架,現爲 Github上的獨立項目。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。
也就是說,Netty 是一個基於NIO的客戶、服務器端編程框架,使用Netty 可以確保你快速和簡單的開發出一個網絡應用,例如實現了某種協議的客戶、服務端應用。Netty相當於簡化和流線化了網絡應用的編程開發過程,例如:基於TCP和UDP的socket服務開發。
我Netty主要是與機器進行通訊 一臺服務對多臺機器
作爲一個開發者,我以業務實現爲主 上代碼
NettyServer .java
package com.springboot.exam.controller.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* date: 2019-07-11 11:11
**/
@Component
public class NettyServer {
private static final Logger log = LoggerFactory.getLogger(NettyServer.class);
//boss事件輪詢線程組
private EventLoopGroup boss = new NioEventLoopGroup();
//worker事件輪詢線程組
private EventLoopGroup worker = new NioEventLoopGroup();
private Channel channel;
//連接map
public static Map<String, ChannelHandlerContext> map = new HashMap<String, ChannelHandlerContext>();
@Value("${n.port}")
private Integer port;
@Value("${n.url}")
private String url;
/**
* 開啓Netty服務
*
* @return
*/
public ChannelFuture start() throws InterruptedException {
// 1. 創建一個線程組:接收客戶端連接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 2. 創建一個線程組:處理網絡操作
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 3. 創建服務器端啓動助手來配置參數
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)//設置兩個線程組
.channel(NioServerSocketChannel.class)//5.使用NioServerSocketChannel作爲服務器端通道的實現
.option(ChannelOption.SO_BACKLOG,1024)//6.設置線程隊列中等待連接的個數
.childOption(ChannelOption.SO_KEEPALIVE,true)//7.保持活動連接狀態
.childHandler(new ChannelInitializer<SocketChannel>() {//8.創建一個通道初始化對象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {//9.往pipeline鏈添加自定義的handler類
//socketChannel.pipeline().addLast(new LineBasedFrameDecoder(10010));
//字符串解碼和編碼
//LineBasedFrameDecoder + StringDecoder 就是一個按行切換的文本解碼器。
//socketChannel.pipeline().addLast( new StringDecoder());
//socketChannel.pipeline().addLast( new StringEncoder());
socketChannel.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture cf = b.bind(port).sync();//綁定端口 非阻塞
//ChannelFuture cf = b.bind(url, port);
ChannelFuture channelFuture1 = cf.syncUninterruptibly();//接收連接
channel = channelFuture1.channel();//獲取通道
if (channelFuture1 != null && channelFuture1.isSuccess()) {
log.info("Netty server 服務啓動成功,端口port = {}", port);
} else {
log.info("Netty server start fail");
}
cf.channel().closeFuture().sync();//異步
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
return cf;
}
/**
* 停止Netty服務
*/
public void destroy() {
if (channel != null) {
channel.close();
}
worker.shutdownGracefully();
boss.shutdownGracefully();
log.info("Netty server shutdown success");
}
}
重點
NettyServerHandler
package com.springboot.exam.controller.netty;
import com.springboot.exam.service.LoginService;
import com.springboot.exam.util.DemoHandler;
import com.springboot.exam.util.util;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//存儲全局
public static final Map<Object,ChannelHandlerContext> mapsocket = new HashMap<>();
// 讀取數據事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf bufferBuf = (ByteBuf) msg;
byte[] data = new byte[bufferBuf.readableBytes()];
bufferBuf.readBytes(data);
System.out.println("解密前");
for (byte sss:data) {
System.out.print(sss+" ");
}
System.out.println();
//業務邏輯
//記得一定要存ChannelHandlerContext
//後期要通過ChannelHandlerContext 發消息給固定的機器
mapsocket.put("發送信息過來的機器的主鍵id",ctx);
}
//發送消息
//xxs 發送的消息(指令)
//ss 發送信息過來的機器的主鍵id(可以用其他主鍵代替,只要和上面對應)
public static void pudate(byte[] xxs,int ss){
ByteBuf msg = null;
msg = Unpooled.buffer(xxs.length);
msg.writeBytes(xxs);
mapsocket.get(ss).writeAndFlush(msg);
}
public static byte[] bl_zjl="000000".getBytes();
public byte[] getBl_zjl() {
return bl_zjl;
}
@Autowired
private LoginService loginServers;
// 數據讀取完畢事件
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
/*byte[] req = getBl_zjl();
ByteBuf msg = null;
msg = Unpooled.buffer(req.length);
msg.writeBytes(req);
ctx.writeAndFlush(msg);*/
}
// 異常發生事件
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close(); //關閉上下文,上下文是所有信息的彙總
super.exceptionCaught(ctx, cause);
System.out.println("異常了!!!!!!!!!!!!!!!");
}
/**
* 將指定byte數組以16進制的形式打印到控制檯
*
* @param hint
* String
* @param b
* byte[]
* @return void
*/
public static void printHexString(String hint, byte[] b) {
System.out.print(hint);
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
System.out.print(hex.toUpperCase() + " ");
}
System.out.println("");
}
/**
*
* @param b
* byte[]
* @return String
*/
public String Bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += " 0x" + hex.toUpperCase();
}
return ret;
}
/**
* 將兩個ASCII字符合成一個字節; 如:"EF"–> 0xEF
*
* @param src0
* byte
* @param src1
* byte
* @return byte
*/
public byte uniteBytes(byte src0, byte src1) {
byte _b0 = Byte.decode("0x" + new String(new byte[] {src0})).byteValue();
_b0 = (byte) (_b0 << 4);
byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
byte ret = (byte) (_b0 ^ _b1);
return ret;
}
/**
* 將指定字符串src,以每兩個字符分割轉換爲16進制形式 如:"2B44EFD9" –> byte[]{0x2B, 0×44, 0xEF,
* 0xD9}
*
* @param src
* String
* @return byte[]
*/
public byte[] HexString2Bytes(String src) {
if (null == src || 0 == src.length()) {
return null;
}
byte[] ret = new byte[src.length() / 2];
byte[] tmp = src.getBytes();
for (int i = 0; i < (tmp.length / 2); i++) {
ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
}
return ret;
}
public static final Map<Object,String> sdsf = new HashMap<>();
public static void main(String[] args){
sdsf.put("11","123");
sdsf.put("22","123456");
sdsf.put("23","123456789");
System.out.println(sdsf.get("11"));
System.out.println(sdsf.get("22"));
System.out.println(sdsf.get("23"));
}
}
NettyClientHandler.java
package com.springboot.exam.controller.netty;
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 NettyClientHandler extends ChannelInboundHandlerAdapter {
// 通道就緒事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client: "+ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("00 0D BC BB BC BC BF AC 82 DD 87 5C C9 01 05", CharsetUtil.UTF_8));
ctx.writeAndFlush(Unpooled.copiedBuffer("00 0D BC BB BC BC BF AC 82 DD 87 5C C9 01 05", CharsetUtil.UTF_8));
}
// 讀取數據事件
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
System.out.println("服務器端發來的消息:"+buf.toString(CharsetUtil.UTF_8));
}
}
NettyClient.java
package com.springboot.exam.controller.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
public static void main(String[] args) throws Exception {
// 1. 創建一個線程組
EventLoopGroup group = new NioEventLoopGroup();
// 2. 創建客戶端的啓動助手,完成相關配置
Bootstrap b = new Bootstrap();
b.group(group) // 3.設置線程組
.channel(NioSocketChannel.class) //4.設置客戶端通道的實現類
.handler(new ChannelInitializer<SocketChannel>() {// 5.創建一個通道初始化對象
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyClientHandler());//6.往pipeline鏈中添加自定義的handler
}
});
System.out.println("........Client is ready............");
// 7.啓動客戶端去連接服務器端,connect方法是異步的 ,sync方法是同步阻塞的
ChannelFuture cf = b.connect("127.0.0.1",10010).sync();
// 8.關閉連接(異步非阻塞)
cf.channel().closeFuture().sync();
}
}
Application.java
package com.springboot.exam;
import com.springboot.exam.controller.netty.NettyServer;
import io.netty.channel.ChannelFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.CommandLineRunner;
/**
* ClassName: SpringBootApplication
* description:
* author:
* date: 2018-09-30 09:15
**/
@org.springframework.boot.autoconfigure.SpringBootApplication//@EnableAutoConfiguration @ComponentScan
public class ExamApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ExamApplication.class, args);
}
@Autowired
NettyServer nettyServer;
@Override
public void run(String... args) throws Exception {
ChannelFuture start = nettyServer.start();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
nettyServer.destroy();
}
});
start.channel().closeFuture().syncUninterruptibly();
}
}