springboot集成netty实现物联网设备数据接收、下发指令(控制)、多开监听端口、区分端口

3 篇文章 0 订阅
1 篇文章 0 订阅

引入依赖jar

		<dependency>
		    <groupId>io.netty</groupId>
		    <artifactId>netty-all</artifactId>
		    <version>4.1.32.Final</version>
		</dependency>
		<dependency>
		   <groupId>org.springframework.boot</groupId>
		   <artifactId>spring-boot-configuration-processor</artifactId>
		   <optional>true</optional>
		</dependency> 

开启要监听的端口

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

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;
@Component
public class NettyStart {
		    @Resource
		    private ServerHandler serverHandler;
		    private EventLoopGroup bossGroup = new NioEventLoopGroup();
		    private EventLoopGroup workGroup = new NioEventLoopGroup();
		    
		    /**
		     * 启动netty服务
		     * @throws InterruptedException
		     */
		    @PostConstruct
		    public void start() throws InterruptedException {
		        ServerBootstrap b=new ServerBootstrap();
		        b.group(bossGroup,workGroup)
		                .channel(NioServerSocketChannel.class)
		                .option(ChannelOption.SO_BACKLOG,128)
		                .childHandler(new ChannelInitializer<SocketChannel>()  {
		                    @Override
		                    protected void initChannel(SocketChannel socketChannel) throws Exception {
		                        socketChannel.pipeline().addLast(serverHandler);
		                    }
		                });
		        ChannelFuture future = b.bind(9011).sync();//开启需要监听 的端口  
		        ChannelFuture future1 = b.bind(9012).sync();//开启需要监听 的端口 多开端口
		        if (future.isSuccess()) {
		            	System.out.println("启动 9011成功");
		        }
		        if (future1.isSuccess()) {
		        	System.out.println("启动 9012成功");
		    	}
		    }
		    
		    /**
		     * 销毁
		     */
		    @PreDestroy
		    public void destroy() {
		        bossGroup.shutdownGracefully().syncUninterruptibly();
		        workGroup.shutdownGracefully().syncUninterruptibly();
		        System.out.println("关闭 Netty 成功");
		    }
}

实现类

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.yun.Util.TimeUtile;
import com.yun.admin.entity.Product_data;
import com.yun.admin.service.impl.Product_dataServiceImpl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import net.sf.json.JSONObject;

//监听 并写入数据库
@Component
@Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {
		    //此处注入数据源操作sql   执行插入设备上传的数据
		    @Resource
		    private Product_dataServiceImpl product_dataServiceImpl;
		    //  将当前客户端连接 存入map   实现控制设备下发 参数
		    public  static Map<String, ChannelHandlerContext> map = new HashMap<String, ChannelHandlerContext>();
		   
		    /**
		     * 获取数据
		     * @param ctx 上下文
		     * @param msg 获取的数据
		     * @throws UnsupportedEncodingException 
		     */
		    @Override
		    public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException{
		    	//msg为接收到的客户端传递的数据  这边直接传的json 数据
		    	ByteBuf readMessage= (ByteBuf) msg;
		    	//解析客户端json 数据
		        JSONObject json=JSONObject.fromObject(readMessage.toString(CharsetUtil.UTF_8));
		        System.out.println("接收到的数据"+readMessage.toString(CharsetUtil.UTF_8));
		        
		        //获取客户端的请求地址  取到的值为客户端的 ip+端口号
		        String url=ctx.channel().remoteAddress().toString();//设备请求地址(个人将设备的请求地址当作 map 的key)
		       //将当前的设备ip+端口存进map  当做下发设备的标识的key
		       //如果已经存在就不用重复存入map
		        if(map.get(url)==null){
		        		map.put(url, ctx);
		    	}
		        int users=0; 
		        //设备请求的 服务器端的地址   用作监听设备请求的那个端口  
		        String servicePort=ctx.channel().localAddress().toString();
		        //判断端口如果客户端请求的端口号为9011  
		         System.out.println("向:"+servicePort.substring(servicePort.length()-4, servicePort.length())+" 端口写入数据");
		        if(servicePort.substring(servicePort.length()-4, servicePort.length()).equals("9011")){
				        	Product_data product_data=new Product_data(); 
			        		//设备请求地址  存入数据库  下方controller层 通过设备id查询此地址   取到map种存入的 ChannelHandlerContext 实现数据下发
					        product_data.setUrl(url);
					        product_data.setJson(readMessage.toString(CharsetUtil.UTF_8));//设备请求时原生数据  
					        product_data.setDeviceID(json.get("deviceID").toString());//设备数据1
					        product_data.setPower1(json.get("power1").toString());//设备数据2
					        product_data.setPower2(json.get("power2").toString());//设备数据3
					        product_data.setPower3(json.get("power3").toString());//设备数据4
					        product_data.setAcquisitionTime(TimeUtile.showDate());//时间 (个人整的当前时间工具类  替换成自己获取当前时间的方法即可)
					        users = product_dataServiceImpl.add_Device_shuju(product_data);
		         }else{
		        	 		Product_data product_data=new Product_data(); 
		        	 		//设备请求地址  存入数据库  下方controller层 通过设备id查询此地址   取到map种存入的 ChannelHandlerContext 实现数据下发
					        product_data.setUrl(url);
					        product_data.setJson(readMessage.toString(CharsetUtil.UTF_8));//设备请求时原生数据  
					        product_data.setDeviceID(json.get("deviceID").toString());//设备数据1
					        product_data.setData1(json.get("data1").toString());//设备数据2
					        product_data.setData2(json.get("data2").toString());//设备数据3
					        product_data.setData3(json.get("data3").toString());//设备数据4
					        product_data.setAcquisitionTime(TimeUtile.showDate());//时间 (个人整的当前时间工具类  替换成自己获取当前时间的方法即可)
					        users = product_dataServiceImpl.add_Device_data(product_data);
		         }
		        String rmsg;
		        if(users>0){
		        		rmsg="11 02 00 C4 00 16 ";//返回成功的信息
		         }else{
		        	 	rmsg="0";//返回失败的信息
		         }
		        ByteBuf message= Unpooled.copiedBuffer(rmsg.getBytes());//处理返回的信息
		      	 //ctx.write(in2);//返回信息 
		        ctx.writeAndFlush(message);//返回信息 
			   	 //刷新缓存区
			   	ctx.flush();
		    }
		    
		    @Override
		    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		   		 cause.printStackTrace();
		   		 ctx.close();
		    }
}

至此就可以实现 服务器端接受设备传递的数据了 ,服务端 可以根据 当前电脑ip地址 +9011(个人开的9011端口 )就可以发送数据并写入数据库了

接下来是服务端向设备发送指令

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.yun.Util.Result;
import com.yun.Utile.SheBei.ServerHandler;
import com.yun.admin.entity.Product_data;
import com.yun.admin.service.impl.Product_dataServiceImpl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

@Controller
@RequestMapping("/equipmenContro")
public class EquipmentController {
		 
		@Autowired 
		private Product_dataServiceImpl product_dataServiceImpl;
		
		@RequestMapping(value="/equipment",method=RequestMethod.POST)
		public @ResponseBody Result equipment(Product_data product_data){
			 //入参 设备id   根据设备id  查询设备最后一次录入数据时候的 ip(url)地址  实现下发
		     Product_data product=product_dataServiceImpl.select_Product_data_url(product_data);
		     if(product!=null){
		     	//需要给设备发送的 16进制数据
		     	String msg=" 16 27 88 90 12 45 31 15 41 ";
		     	//转码
		    	 ByteBuf message= Unpooled.copiedBuffer(msg.getBytes());
		    	 //执行设备控制    根据product.getUrl() 上个类写入map  的key  取到map中的 ChannelHandlerContext  执行writeAndFlush发送数据
				 ServerHandler.map.get(product.getUrl()).channel().writeAndFlush(message);
				 return Result.toClient(1,"成功");
		     }else{
		    	 return  Result.toClient(0,"失败");
		     }
			
		}
}

**OK完成 附上效果图 **
1、 启动
在这里插入图片描述
2、客户端发起连接 并发送数据
在这里插入图片描述

3、服务端接到数据 打印
在这里插入图片描述4、服务器向客户端发送数据 实现设备控制 直接调用最后controller中的请求地址 然后客户端会打印接收到的数据

在这里插入图片描述
*结束、 有疑问请留言 **
**注:仅作参考 **

### 回答1: Spring Boot集成Netty可以实现TCP协议的通信。Netty是一个高性能、异步事件驱动的网络应用框架,可以用于开发各种协议的服务器和客户端。在Spring Boot中,可以通过添加Netty依赖和配置Netty的相关参数来实现TCP通信。具体实现方式可以参考相关文档和示例代码。 ### 回答2: Spring Boot是一种快速开发框架,旨在简化应用程序的开发和部署过程。Netty是一个事件驱动的网络应用程序框架。它允许您快速构建高性能和可扩展的网络应用程序。在本文中,我们将探讨如何使用Spring Boot集成Netty实现TCP。 首先,我们需要在pom.xml文件中添加NettySpring Boot的依赖。这有助于我们在代码中使用Spring BootNetty的类和方法。例如,我们需要添加以下依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.57.Final</version> </dependency> </dependencies> ``` 接下来,我们将创建一个Netty服务器类来处理传入的TCP连接。以下是一个简单的Netty服务器类: ```java @Component public class NettyServer { private final int port; @Autowired public NettyServer(@Value("${netty.port:8000}") int port) { this.port = port; } public void start() throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder(), new StringEncoder(), new NettyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 100) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ``` 在此代码中,我们创建了一个Netty服务器来处理传入的TCP连接。我们使用@Value注释将端口号设置为默认值8000。在start()方法中,我们创建了两个事件循环组,一个用于接受传入连接,另一个用于处理连接。我们还将服务器绑定到指定的端口,并在服务器关闭时优雅地关闭事件循环组。 接下来,我们需要创建一个Netty处理程序类来处理传入的数据。以下是一个简单的Netty处理程序类: ```java @Component public class NettyServerHandler extends SimpleChannelInboundHandler<String> { @Override public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("Received message: " + msg); ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` 在这个代码中,我们创建了一个Netty服务器处理程序类来处理传入的数据。我们使用SimpleChannelInboundHandler类来处理数据。在channelRead0()方法中,我们打印接收到的消息并将消息写回客户端。在channelReadComplete()方法中,我们将缓冲区的所有数据刷新到远程节点并关闭通道。在exceptionCaught()方法中,我们打印了异常堆栈并关闭通道。 最后,我们需要创建一个Spring Boot应用程序类来启动Netty服务器。以下是一个简单的Spring Boot应用程序类: ```java @SpringBootApplication public class Application { public static void main(String[] args) throws InterruptedException { SpringApplication.run(Application.class, args); NettyServer server = new NettyServer(8000); server.start(); } } ``` 在此代码中,我们启动了Spring Boot应用程序并创建了一个Netty服务器对象。我们在启动服务器时将端口号设置为8000。注意,我们使用的是阻塞式服务器,并且它会阻塞主线程,直到服务器被关闭。 综上所述,我们使用Spring Boot集成Netty实现了TCP。我们创建了一个Netty服务器类来处理传入的TCP连接,一个Netty处理程序类来处理传入的数据,最后创建了一个Spring Boot应用程序类来启动Netty服务器。 ### 回答3: Spring Boot是一套快速开发框架,它可以帮助开发人员更快速、更高效地开发应用程序。而Netty是一个高性能的网络框架,可以实现异步、事件驱动的网络编程。将Spring BootNetty结合在一起,可以实现快速构建高性能的网络应用程序。 想要将Spring Boot集成Netty实现TCP,有以下几个步骤: 1. 添加Netty依赖:在pom.xml中添加Netty的依赖,可以从官网上找到最新版本的依赖。 2. 编写Netty处理器:首先需要编写一个Netty的处理器类,来处理客户端的请求。处理器类需要实现NettyChannelInboundHandler接口,并重写其中的channelRead方法,在该方法中处理来自客户端的数据。 3. 配置Netty服务器:在程序的入口类中,创建Netty服务器并配置相关参数,如端口号、主机地址等。同时,需要将编写的处理器类设置进来,以便在接收到客户端的请求时能够正确调用。 4. 运行程序:配置好之后,可以运行程序,测试TCP协议的功能是否正常。此时可以使用各种工具进行测试,如Telnet、Putty等。 综上所述,使用Spring Boot集成Netty实现TCP,可以大大简化应用程序的开发流程,同时也能够实现高性能的网络编程。如果开发人员想要实现更复杂的网络应用程序,可以在Netty处理器中添加更多的功能逻辑,并根据具体的业务需求进行开发。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值