netty+springboot+dubbo+zk整合tcp项目

1.描述

     为了能快速开发netty集成springboot整合tcp项目,本篇博客搭建勒一个空的架子,只需要修改对应的业务逻辑代码就行,很好的提高勒开发速度,降低勒netty开发的难度。开发人员,只需要关注于业务逻辑就行。

      建议:有一定的netty和springboot,dubbbo基础。

     下载socket调试工具

给一个测试的demo,十六进制字符串

2454585858007A0E320460047A3F00000330473336303020646D67642000000BE7130C040F0A00000000002D00390021004D0024004B0026005200300090060026005309000000000000000000000000000004AA04AA0A04A80700484A05470700278227830427820A000057B800559E0050C50500840D0A0000

2.netty

     2.1  服务器端   

              2.1.1 pom.xml 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.cloudtech</groupId>
	<artifactId>dipper</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>dipper</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-starter -->
		<dependency>
			<groupId>com.alibaba.boot</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>0.2.0</version>
		</dependency>


		<!-- netty -->
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>5.0.0.Alpha2</version>
		</dependency>

		<!-- 配置提示 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

	<build>
	   <!-- 打成jar包的名称 -->
  		<finalName>dipper</finalName>
		<plugins>
			<!-- 解决web.xml报错的问题 -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin </artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

2.1.2  application.yml

####自定义配置           
netty:
  port: 4002    ###分钟监听端口
  module-type: 0    ####0标识主通讯模块   1标识从通讯模块
  

stationTypeId: 17
###最大缓存长度
maxBuffLen: 10240  
  
logging:
  config: classpath:logback-boot.xml
####dubbot版本
demo:
  service:
   version:  1.0.0
 
dubbo:
  reference:
    check: false
    com:
     cloudtech:
       web:
         dubbo:
           check: false
  consumers:
    check: false
  scan:
   basePackages: com.cloudtech.web.dubbo     
  application:
    id: dipper-provider-demo 
    name: dipper-provider-demo
  provider:
    retries: -1
    timeout: 30000000   
  protocol:
    id: dubbo 
    name: dubbo
    port: -1
  registry:
    protocol: zookeeper
    check:  false
    address: zookeeper://127.0.0.1:2181  

2.1.3 启动类

package com.cloudtech.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ComponentScan;

import com.cloudtech.config.NettyConfig;
import com.cloudtech.server.Server;
import com.cloudtech.util.Consts;

@SpringBootApplication
@ComponentScan(value = {"com.cloudtech"})
public class DipperApplication implements CommandLineRunner {
	protected static final Logger LOGGER = LoggerFactory.getLogger(DipperApplication.class);
	
	@Autowired
	NettyConfig nettyConfig;
	
	@Autowired
	Server server;
	
	@Value("${stationTypeId}")
	private Integer stationTypeId;
	@Value("${maxBuffLen}")
	private Integer maxBuffLen;
	
	public static void main(String[] args) {
		new SpringApplicationBuilder(DipperApplication.class)
	    .web(WebApplicationType.NONE)
	    .run(args);
		//SpringApplication.run(Aws310Application.class, args);
	}
    
	/**
	 * spring boot启动后,会进入该方法
	 */
	@Override
	public void run(String... args) throws Exception {
		init();
		//加载分钟监听线程
		new Thread("Dipper-recevice-thread") {
			@Override
			public void run() {
				super.run();
				server.start(Consts.SERVER_PORT);
			}
		}.start();
	}

	/**
	 * 初始化信息
	 */
	@SuppressWarnings("unused")
	private void init() {
		Consts.SERVER_PORT = nettyConfig.getPort();
		Consts.MODULE_TYPE = nettyConfig.getModuleType();
		Consts.STATION_TYPE_ID = stationTypeId;
		Consts.MAX_BUFF_LEN = maxBuffLen;
	}

	
}
package com.cloudtech.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * 读取yml配置文件中的信息
* @ClassName: NettyConfig  
* @Description:   
* @author wude  
* @date 2018年9月19日  
*
 */
@Component
@PropertySource(value={"classpath:application.yml"})
public class NettyConfig {
    @Value("${netty.port}")
    private int port;
    @Value("${netty.module-type}")
    private int moduleType;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

	public int getModuleType() {
		return moduleType;
	}

	public void setModuleType(int moduleType) {
		this.moduleType = moduleType;
	}
}
package com.cloudtech.util;

import com.cloudtech.enums.ModuleType;

public class Consts {
	/** 新版本服务器端口号 */
	public static Integer SERVER_PORT = 4000;
	/** 保存session的的属性值 */
	public static final String PILE_ID = "pileId";
	/** 请求成功 **/
	public static Integer SUCCESS = 1;
	/** 请求失败 **/
	public static Integer FAILED = 0;
	/** 主模块  */
	public static Integer MODULE_TYPE = ModuleType.MASTER.getCode();
	/** 模块类型id  */
	public static Integer STATION_TYPE_ID = 7;
	/** 设置最大缓存长度  */
	public static Integer MAX_BUFF_LEN=10240;
}
package com.cloudtech.server;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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 io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * 
* @ClassName: Server  
* @Description:netty服务端   
* @author wude  
* @date 2018年9月7日  
*
 */
@Component
public class Server {
	protected static final Logger logger = LoggerFactory.getLogger(Server.class);
	@Autowired
	private ServerHandler serverHandler;

	
	/**
	 * 启动
	 */
	public void start(int port) {

		// 服务类
		ServerBootstrap b = new ServerBootstrap();

		// 创建boss和worker
		EventLoopGroup bossGroup = new NioEventLoopGroup();
 		EventLoopGroup workerGroup = new NioEventLoopGroup();
		//业务线程池,实现消息串行化
		EventLoopGroup busyGroup = new NioEventLoopGroup();

		try {
			// 设置循环线程组事例
			b.group(bossGroup, workerGroup);

			// 设置channel工厂
			b.channel(NioServerSocketChannel.class);

			// 设置管道
			b.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				public void initChannel(SocketChannel ch) throws Exception {
					//第一个参数 读超时
					//第二个参数 写超时
					//第三个参数读写超时
					ch.pipeline().addLast(new IdleStateHandler(5, 5, 60));
					ch.pipeline().addLast(new RequestDecoder());
					//ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Consts.MAX_BUFF_LEN,delimiter));
					ch.pipeline().addLast(new StringDecoder());
					ch.pipeline().addLast(busyGroup,serverHandler);
				}
			});

			b.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小
			b.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接
			b.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送

			logger.info("启动Dipper采集成功!port={}",port);
			//绑定端口
			ChannelFuture future = b.bind(port);
			
			//等待服务端关闭
			future.channel().closeFuture().sync();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			//释放资源
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
			busyGroup.shutdownGracefully();
		}
	}

}

2.2.3  编码器 

package com.cloudtech.server;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudtech.util.BufferUtil;
import com.cloudtech.web.vo.RequestVo;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
 * 数据包解码器
 * <pre>
 * 数据包格式
 * +——----——--------+——-----——+——----———+——----——-+-----------+---------+--------+---------|--------|-------
 * |内容(5)|整个包长度(n)|用户地址 (3)|信息类别(1)|发信方地址(3)| 发信时间(2)|电文长度(n-20)|电文内容|CRC校验(1)|校验和(1)
 * +——----——--------+——-----——+——----———+——----——-+-----------+---------+--------+---------|--------|-------
 * 
 * 
 * </pre>
 *  一个完整包的长度至少为20
 *  
* @author wude  
* @date 2018年9月7日  
 * 
 */
public class RequestDecoder extends ByteToMessageDecoder {
	protected static Logger LOGGER = LoggerFactory.getLogger(RequestDecoder.class);
	
	/**
	 * 数据包基本长度
	 */
	public static int BASE_LENTH = 20;

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
		//防止socket攻击,也就是说可能会存在有人恶意攻击程序,定义Integer最大值,向服务器发请求,造成服务器cpu,内存的开销增多
		try {
			if(buffer.readableBytes() > 2048){
				buffer.skipBytes(buffer.readableBytes());
			}
		/*	while(true){*/
				if(buffer.readableBytes() >= BASE_LENTH){   //包最小长度应为20
					//第一个可读数据包的起始位置
					while(true) {
						//标记初始读游标位置
						buffer.markReaderIndex();
						
						String str = BufferUtil.readFixLenAscStr(buffer, 5);
						//判断头部
						if(str.equals("$TXXX")){   //通讯信息
							buffer.resetReaderIndex();
							break;
						}
						//未读到包头标识略过一个字节
						buffer.resetReaderIndex();
						buffer.readByte();
						//不满足
						if(buffer.readableBytes() < BASE_LENTH - 5){
							return;
						}
					}
					//标记初始读游标位置
					buffer.markReaderIndex();
					
					//内容
					String content = BufferUtil.readFixLenAscStr(buffer, 5);
					//长度
					Short len = buffer.readShort();
					
					if(buffer.readableBytes() <len-7){
						return;
					}
					RequestVo requestVo = new RequestVo();
					//地址
					String userAddress =BufferUtil.readFixHexLenStr(buffer, 3);
					//信息类别
					byte infoType = buffer.readByte();
					//发信方地
					String sendStr = BufferUtil.readFixHexLenStr(buffer, 3);
					//发信时间
					byte hh = buffer.readByte();
					byte mm = buffer.readByte();
					//电文长度
					int contenLen = buffer.readShort() / 8;
					if(len !=(contenLen + 20) || contenLen <= 78){  
						//说明报文长度不对
						buffer.resetReaderIndex();
						BufferUtil.readFixLen(buffer, len);
						return;
					}
					//数据
					byte[] datas = new byte[contenLen];
					buffer.readBytes(datas);
					//CRC校验
					byte crc = buffer.readByte();
					//校验和
					byte checkSum = buffer.readByte();
					requestVo.setContent(content);
					requestVo.setLen(len.intValue());
					requestVo.setUserAddress(userAddress);
					requestVo.setInfoType(infoType);
					requestVo.setSendAddress(sendStr);
					requestVo.setHh(hh);
					requestVo.setMm(mm);
					requestVo.setContenLen(contenLen);
					
					requestVo.setData(datas);
					requestVo.setCrc(crc);
					requestVo.setCheckSum(checkSum);
					
					buffer.resetReaderIndex();
					//原始报文
					String bytes2Hex = BufferUtil.readFixHexLen(buffer, len);
					requestVo.setReportStr(bytes2Hex);
					
					out.add(requestVo);
				}else{
					//数据不完整,等待完整的数据包
					return;
				}
		/*	}*/
			//数据不完整,等待完整的数据包
			return;
		} catch (Exception e) {
			return;
		}
	}
}

 2.2.4 业务处理类

package com.cloudtech.server;

import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.cloudtech.server.comm.ModuleId;
import com.cloudtech.server.scanner.Invoker;
import com.cloudtech.server.scanner.InvokerHoler;
import com.cloudtech.server.serial.BufferFactory;
import com.cloudtech.server.session.Session;
import com.cloudtech.server.session.SessionImpl;
import com.cloudtech.util.BufferUtil;
import com.cloudtech.web.dubbo.BaseDataResult;
import com.cloudtech.web.vo.RequestVo;
import com.cloudtech.web.vo.StationVo;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * 
 * @ClassName: SessionManager
 * @Description: 消息接受处理类
 * @author wude
 * @date 2018年9月7日
 */
@Component
public class ServerHandler extends SimpleChannelInboundHandler<RequestVo> {
	private static Logger LOGGER = LoggerFactory.getLogger(ServerHandler.class);
   	@Reference(version = "${demo.service.version}",timeout=30000,check=false)
	DubboService dubboService;

	@Override
	protected void messageReceived(ChannelHandlerContext ctx, RequestVo requestVo) throws Exception {
		System.out.println(requestVo.toString());
		byte[] data = requestVo.getData();
		ByteBuf buffer = BufferFactory.getBuffer(data);
		String stationNum = BufferUtil.readFixLenStr(buffer, 5);
		System.out.println(stationNum);
	}
}

@Component标识加入容器中

可再接收内中,调用dubboserver接口

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值