springboot+Netty搭建web服务器实现物联网温湿度采集

前言:这段时间做了一个课程设计,内容是将温湿度传感器采集到的温湿度数据上传到web服务器并以表格或者折线图的方式可视化展示出来。话不多说:上代码!!!

①Netty服务器搭建

NettyServer.java

/**
 * @author cx
 * @Time 2020/6/29 22:00
 * @Description netty 服务器配置
 */
public class NettyServer {
    public void start(InetSocketAddress socketAddress){
        /**new 一个主线程组*/
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        /**new 一个工作线程组*/
        EventLoopGroup workGroup = new NioEventLoopGroup(200);
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInitializer())
                .localAddress(socketAddress)
                /**设置队列的大小*/
                .option(ChannelOption.SO_BACKLOG,1024)
                /**两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文*/
                .childOption(ChannelOption.SO_KEEPALIVE,true);
                /**绑定端口,开始接收进来的连接*/
                try{
                    ChannelFuture future = bootstrap.bind(socketAddress).sync();
                    System.out.println("服务器ip为:"+socketAddress.getHostName());
                    System.out.println("服务器端口号为:"+socketAddress.getPort());
                    future.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    /**关闭主线程组*/
                    bossGroup.shutdownGracefully();
                    /**关闭工作线程组*/
                    workGroup.shutdownGracefully();
                }
    }
}

NettyServerHandler.java

/**
 * @author cx
 * @Time 2020/6/29 22:23
 * @Description 服务端业务处理
 */
@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    /**获取实例化对象*/
    @Autowired
    protected IHumitureService humitureService;

    private static NettyServerHandler serverHandler;

    /**配合@Component注解获取service层的bean*/
    @PostConstruct
    public void init(){
        serverHandler = this;
        serverHandler.humitureService = this.humitureService;
    }

    /**
     * 客户端连接会触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端发起连接!!!!");
    }

    /**
     * 客户端发消息会触发
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        /**获取客户端的IP*/
        InetSocketAddress insocket = (InetSocketAddress)ctx.channel().remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        /**将温湿度数据处理*/
        String tem = msg.toString();
        String[] arr = tem.split(",");
        Humiture  humiture = new Humiture();
        humiture.setTemp(arr[0]);
        humiture.setHumidity(arr[1]);
        humiture.setIp(ip);
        /**调用业务层方法将数据写入数据库*/
        serverHandler.humitureService.insertData(humiture);
        System.out.println("服务器接收到客户端的温度,湿度---"+msg.toString());
        System.out.println("温湿度写入数据库成功!!!!");
        ctx.write("receive OK!");
        ctx.flush();
    }

    /**
     * 发生异常触发
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

注意:上面代码解决了一个问题,就是如何在controller层外获得bean,然后调用业务层方法将获得的数据写入数据库。方法是在handler类上加一个@Component注解,然后写一个init()方法,init()方法上面加一个@PostConstruct注解将bean实例化

ServerChannelInitializer .java

/**
 * @author cx
 * @Time 2020/6/29 22:06
 * @Description 初始化编码器
 */
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        /**添加编解码*/
        socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        socketChannel.pipeline().addLast(new NettyServerHandler());
    }
}

springbootApplication

package com.hunau.springboot;

import com.hunau.springboot.nettyserver.NettyServer;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.net.InetSocketAddress;

@SpringBootApplication
@MapperScan("com.hunau.springboot.mapper")
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
        /**启动服务端*/
        NettyServer nettyServer = new NettyServer();
        nettyServer.start(new InetSocketAddress("127.0.0.1",8087));
    }
}

②controller层代码

HumitureController .java

/**
 * @author cx
 * @Time 2020/6/28 15:32
 * @Description 温湿度采集 控制层
 */
@Controller
public class HumitureController {
    @Autowired
    private IHumitureService iHumitureService;

    @GetMapping("/test")
    @ResponseBody
    public List<Humiture> list()
    {
        return iHumitureService.listHumiture();
    }

    @GetMapping("/list")
    @ResponseBody
    public Map<String,Object> selectAll()
    {
        Map<String,Object> map = new HashMap<>();
        List<Humiture> data = iHumitureService.listHumiture();
        map.put("code",0);
        map.put("msg","随便写点东西");
        map.put("count",10);
        map.put("data",data);
        return map;
    }
}

IndexController .java

/**
 * @author cx
 * @Time 2020/6/28 10:22
 * @Description 页面信息展示 控制层
 */
@Controller
public class IndexController {

    @Autowired
    private IHumitureService iHumitureService;

    @GetMapping("/")
    public String hello(ModelMap modelMap)
    {
        List<Humiture> list = iHumitureService.listHumiture();
        modelMap.addAttribute("message",list);
        return "index";
    }

    @GetMapping("/humiture")
    public String humiture(ModelMap modelMap)
    {
        List<Humiture> list = iHumitureService.listHumiture();
        modelMap.addAttribute("value",list);
        return "humiture";
    }
}

③实体层
Humiture.java

/**
 * @author cx
 * @Time 2020/6/28 15:11
 * @Description 温湿度检测 实体
 */
public class Humiture {

    /**数据编号*/
    private int id;
    /**温度*/
    private String temp;
    /**湿度*/
    private String humidity;
    /**ip地址*/
    private String ip;
    /**采集时间*/
    private String time;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTemp() {
        return temp;
    }

    public void setTemp(String temp) {
        this.temp = temp;
    }

    public String getHumidity() {
        return humidity;
    }

    public void setHumidity(String humidity) {
        this.humidity = humidity;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }
}

④mapper层

HumitureMapper .java

/**
 * @author cx
 * @Time 2020/6/28 15:18
 * @Description 温湿度采集 数据层
 */
@Repository
public interface HumitureMapper {
    /**
     * @description 温湿度采集 查询
     *
     * @param
     * @return 温湿度采集 列表
     */
    public List<Humiture> listHumiture();
    /**
     * @description 温湿度采集 存库
     *
     * @param
     * @return 温湿度采集 存库
     */
    public int insertData(Humiture humiture);
}

⑤service层代码

IHumitureService .java

/**
 * @author cx
 * @Time 2020/6/28 15:22
 * @Description 温湿度采集 业务层接口
 */
public interface IHumitureService {
    /**
     * @description 温湿度采集 查询
     *
     * @param
     * @return 温湿度采集 列表
     */
    public List<Humiture> listHumiture();
    /**
     * @description 温湿度采集 存库
     *
     * @param
     * @return 温湿度采集 存库
     */
    public int insertData(Humiture humiture);
}

HumitureServiceImpl .java

/**
 * @author cx
 * @Time 2020/6/28 15:23
 * @Description 温湿度采集 业务层实现
 */
@Service
public class HumitureServiceImpl implements IHumitureService {

    @Autowired
    private HumitureMapper humitureMapper;

    /**
     * @description 温湿度采集 查询
     *
     * @param
     * @return 温湿度采集 列表
     */
    @Override
    public List<Humiture> listHumiture()
    {
        return humitureMapper.listHumiture();
    }
    /**
     * @description 温湿度采集 存库
     *
     * @param
     * @return 温湿度采集 存库
     */
    @Override
    public int insertData(Humiture humiture){
        return humitureMapper.insertData(humiture);
    }
}

⑥resource下的mapper

HumitureMapper .xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hunau.springboot.mapper.HumitureMapper">
    <resultMap id="HumitureResult" type="Humiture">
        <result property="id" column="id"/>
        <result property="temp" column="temp"/>
        <result property="humidity" column="humidity"/>
        <result property="ip" column="ip"/>
        <result property="time" column="time"/>
    </resultMap>

    <select id="listHumiture" resultMap="HumitureResult">
        select * from tb_humiture
    </select>

    <insert id="insertData" parameterType="Humiture">
        insert into tb_humiture
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="temp != null  and temp != ''  ">temp,</if>
            <if test="humidity != null  and humidity != ''  ">humidity,</if>
            <if test="ip != null  and ip != ''  ">ip,</if>
            <if test="time != null  and time != ''  ">time,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="temp != null  and temp != ''  ">#{temp},</if>
            <if test="humidity != null  and humidity != ''  ">#{humidity},</if>
            <if test="ip != null  and ip != ''  ">#{ip},</if>
            <if test="time != null  and time != ''  ">#{time},</if>
        </trim>
    </insert>
</mapper>

⑦前端代码

humiture.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>test</title>
    <script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/echarts-all-3.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body> <!--5.jpg-->

<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;position: absolute; top:50%; left: 50%; margin-top: -200px;margin-left: -300px"></div>
<!--<div style="left:945px;top:819px ">-->
<!--    <a href="/" rel="external nofollow" >-->
<!--        <button type="button" class="btn btn-primary ">返回首页</button>-->
<!--    </a>-->
<!--</div>-->
<!--<script th:inline="javascript">-->
<!--    var data = [[${value}]];-->
<!--    console.log(data);-->
<!--</script>-->
<script th:inline="javascript">
    /**基于准备好的dom,初始化echarts实例*/
    var myChart = echarts.init(document.getElementById('main'));
    var data = [[${value}]];
    console.log(data);
    /**温度*/
    var temperature = [];
    /**湿度*/
    var humidity = [];
    /**采集时间*/
    var coltime = [];
    /**获取温度打包为数组*/
    for (i in data)
        temperature.push(data[i].temp);
    console.log(temperature);
    /**获取湿度打包为数组*/
    for (i in data)
        humidity.push(data[i].humidity);
    console.log(humidity);
    for (i in data)
        coltime.push(data[i].time);
    console.log(coltime);
    /**指定图表的配置项和数据*/
    option = {
        title: {
            text: '温湿度动态图'
        },
        tooltip: {
            trigger: 'axis'
        },
        legend: {
            data: ['温度', '湿度']
        },
        grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true
        },
        toolbox: {
            feature: {
                saveAsImage: {}
            }
        },
        xAxis: {
            type: 'category',
            boundaryGap: false,
            data: coltime
        },
        yAxis: {
            type: 'value'
        },
        series: [
            {
                name: '温度',
                type: 'line',
                stack: '总量',
                data: temperature
            },
            {
                name: '湿度',
                type: 'line',
                stack: '总量',
                data: humidity
            }
        ]
    };

    /**使用刚指定的配置项和数据显示图表*/
    myChart.setOption(option);
</script>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymleaf</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <link rel="stylesheet"  th:href="@{/layui.css}">
    <script th:src="@{/layui.js}"></script>
    <script></script>
</head>
<body background="2.jpg"> <!--2.jpg-->
<!--<div style="text-align: center;margin-top: 20px">-->
<!--        <button type="button" class="btn btn-success ">温湿度采集表格数据展示</button>-->
<!--</div>-->
<h1 style="text-align: center;margin-top: 20px">温湿度超声波采集表格数据展示</h1>
<h2 style="text-align: center;margin-top: 20px;color: mediumvioletred">author : cx</h2>
<!--<div style="text-align: center;margin-top: 20px">-->
<!--    <button type="button" class="btn btn-danger ">author:陈霞</button>-->
<!--</div>-->
<table class="table table-hover table-dark" th:align="center" th:valign="middle" style="margin-top: 40px">
    <thead>
    <tr>
        <th scope="col">组号</th>
        <th scope="col">温度</th>
        <th scope="col">湿度</th>
        <th scope="col">ip地址</th>
        <th scope="col">距离</th>
        <th scope="col">采集时间</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="c,State:${message}">
        <th scope="row" th:text="${c.id}"></th>
        <td th:text="${c.temp}"></td>
        <td th:text="${c.humidity}"></td>
        <td th:text="${c.ip}"></td>
        <td th:text="${c.distance}"></td>
        <td th:text="${c.time}"></td>
    </tr>
    </tbody>
</table>
<div style="text-align: center">
    <a href="/humiture" rel="external nofollow" >
        <button type="button" class="btn btn-primary ">点击查看动态折现图</button>
    </a>
</div>
</body>

</html>

application.yml文件配置如下:

server:
  port: 8081

spring:
  resources:
    static-locations: classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources
  datasource:
    username: 填写数据库用户名
    password: 填写密码
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver

mybatis:
  mapper-locations: mapper/HumitureMapper.xml
  type-aliases-package: com.hunau.springboot.domain

#showSql
logging:
  level:
    com:
      example:
        mapper : debug

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hunau</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot</name>
    <description>project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--thymleaf模板-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--网页相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.15</version>
        </dependency>
        <!-- mybatis相关依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--netty依赖-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.0.36.Final</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

代码到此结束
下面上效果图
在这里插入图片描述

效果图2
客户端发送温湿度格式为 字符串,中间以逗号隔开例如“29,88”(温度,湿度)服务器就会接收到对应的温湿度信息并且存入数据库!

都看到这里了,不妨点个赞再走呗,创作不易,不想被白嫖,求点赞,奥利给!!!

  • 61
    点赞
  • 138
    收藏
    觉得还不错? 一键收藏
  • 45
    评论
实现局域网音视频通话可以用Spring Boot作为后端框架,Netty作为网络通信框架,WebSocket作为实现双向通信的协议。以下是一个简单的实现过程: 1. 首先需要搭建一个Spring Boot项目,可以使用Spring Initializr来快速生成项目。在pom.xml中添加NettyWebSocket的依赖,例如: ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建一个WebSocket处理器类,用来处理WebSocket的连接、关闭和消息收发等逻辑。例如: ```java @Component @ServerEndpoint("/video-chat") public class VideoChatHandler { private static final Logger LOGGER = LoggerFactory.getLogger(VideoChatHandler.class); @OnOpen public void onOpen(Session session) { LOGGER.info("WebSocket opened: {}", session.getId()); } @OnMessage public void onMessage(String message, Session session) { LOGGER.info("Received message: {}", message); // TODO: 处理收到的消息 } @OnClose public void onClose(Session session) { LOGGER.info("WebSocket closed: {}", session.getId()); } @OnError public void onError(Throwable error) { LOGGER.error("WebSocket error", error); } } ``` 3. 在Spring Boot的配置类中添加WebSocket的配置,例如: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private VideoChatHandler videoChatHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(videoChatHandler, "/video-chat").setAllowedOrigins("*"); } } ``` 4. 使用Netty实现音视频的传输。可以使用Netty提供的UDP协议来实现多人音视频通话,也可以使用TCP协议来实现点对点的音视频通话。需要根据实际情况选择相应的协议,这里以TCP协议为例: ```java @Component public class VideoChatServer { private static final Logger LOGGER = LoggerFactory.getLogger(VideoChatServer.class); @Value("${server.video-chat.port}") private int port; @PostConstruct public void start() { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // TODO: 添加音视频相关的编解码器和处理器 } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture future = bootstrap.bind(port).sync(); LOGGER.info("Video chat server started on port {}", port); future.channel().closeFuture().sync(); } catch (InterruptedException e) { LOGGER.error("Video chat server interrupted", e); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ``` 5. 在WebSocket处理器中实现音视频数据的收发逻辑。当收到音视频数据时,可以将数据转发给所有连接的WebSocket客户端。例如: ```java @Component @ServerEndpoint("/video-chat") public class VideoChatHandler { private static final Logger LOGGER = LoggerFactory.getLogger(VideoChatHandler.class); private List<Session> sessions = new CopyOnWriteArrayList<>(); @OnOpen public void onOpen(Session session) { LOGGER.info("WebSocket opened: {}", session.getId()); sessions.add(session); } @OnMessage public void onMessage(ByteBuffer buffer, Session session) throws IOException { LOGGER.info("Received video data from {}", session.getId()); byte[] data = new byte[buffer.remaining()]; buffer.get(data); for (Session s : sessions) { if (s.isOpen() && !s.getId().equals(session.getId())) { s.getBasicRemote().sendBinary(ByteBuffer.wrap(data)); } } } @OnClose public void onClose(Session session) { LOGGER.info("WebSocket closed: {}", session.getId()); sessions.remove(session); } @OnError public void onError(Throwable error) { LOGGER.error("WebSocket error", error); } } ``` 6. 在前端页面中使用WebSocket来实现音视频通话。可以使用WebRTC等技术来实现音视频采集、编解码、传输等功能。这里不再赘述。 以上就是一个简单的局域网音视频通话的实现过程。需要注意的是,音视频通话涉及到的技术较多,需要根据实际情况进行选择和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值