智能设备网络发现与绑定技术方案

常用的发现方式

1. Wi-Fi配网 + mDNS/Bonjour发现

典型流程:

  • 设备开启AP模式或SmartConfig配网
  • 连接到家庭Wi-Fi后,通过mDNS广播服务信息
  • 手机App扫描局域网内的mDNS服务
  • 发现设备后进行配对绑定

Java实现示例:

mDNS/DNS-SD java及Avahi 实现服务发布和服务发现_java mdns-优快云博客

2. 蓝牙发现(BLE)

适用场景:

  • 智能手环、手表等穿戴设备
  • 近距离配对场景

发现方式:

  • BLE广播包携带设备信息
  • 手机蓝牙扫描发现设备
  • 通过特征UUID识别设备类型
  • 提供一个静态网页发现蓝牙的示例(使用 Chrome 浏览器(或基于 Chromium 内核的浏览器,如 Edge)而且需要https才能使用)
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>H5 蓝牙扫描</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            padding: 20px;
            background-color: #f4f4f9;
        }
        .container {
            max-width: 600px;
            margin: auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
        }
        #scanButton {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 16px;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        #scanButton:hover {
            background-color: #0056b3;
        }
        #deviceList {
            margin-top: 20px;
            text-align: left;
            border-top: 1px solid #eee;
            padding-top: 10px;
        }
        #deviceList p {
            background-color: #e9ecef;
            padding: 10px;
            border-radius: 5px;
            margin-bottom: 5px;
            word-wrap: break-word;
        }
        #status {
            color: #666;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>H5 蓝牙扫描器</h1>
        <button id="scanButton">开始扫描</button>
        <p id="status">点击按钮开始扫描附近的蓝牙设备。</p>
        <div id="deviceList"></div>
    </div>

    <script>
        const scanButton = document.getElementById('scanButton');
        const deviceList = document.getElementById('deviceList');
        const statusElement = document.getElementById('status');

        scanButton.addEventListener('click', async () => {
            try {
                if (!('bluetooth' in navigator)) {
                    throw new Error('您的浏览器不支持 Web Bluetooth API,请使用 Chrome 等浏览器。');
                }

                statusElement.textContent = '正在扫描...';
                deviceList.innerHTML = '';

                // 修改:不使用 services 过滤,而是直接使用 `acceptAllDevices: true`
                // 这样可以发现所有附近的 BLE 设备,包括那些没有标准服务 UUID 的设备。
                const device = await navigator.bluetooth.requestDevice({
                    acceptAllDevices: true,
                    // 将您的设备可能使用的服务作为 optionalServices 添加
                    optionalServices: ['heart_rate', 'battery_service']
                });

                if (device) {
                    statusElement.textContent = `已连接设备:${device.name || '未知设备'} - ${device.id}`;
                    displayDevice(device.name || '未知设备', device.id);
                } else {
                    statusElement.textContent = '没有发现设备或用户取消了。';
                }

            } catch (error) {
                statusElement.textContent = `扫描失败: ${error.message}`;
                console.error(error);
            }
        });

        function displayDevice(name, id) {
            const p = document.createElement('p');
            p.textContent = `设备名称: ${name}, 设备ID: ${id}`;
            deviceList.appendChild(p);
        }
    </script>
</body>
</html>

3. UDP广播发现

实现方式:

  • 设备定期发送UDP广播包
  • 包含设备ID、类型、IP等信息
  • 客户端监听特定端口接收广播

Java实现(基于Netty框架):

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.31.Final</version>
    <scope>compile</scope>
</dependency>
// ==================== 服务端 ====================
package cn.fuzhi.service;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

/**
 * UDP广播服务端
 * 监听8888端口,接收广播消息并回复
 */
@Slf4j
public class UdpBroadcastServer {
    
    private final int port;
    private EventLoopGroup group;
    private Channel channel;
    
    public UdpBroadcastServer(int port) {
        this.port = port;
    }
    
    /**
     * 启动服务端
     */
    public void start() {
        group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .option(ChannelOption.SO_RCVBUF, 2048)
                    .option(ChannelOption.SO_SNDBUF, 2048)
                    .handler(new ChannelInitializer<NioDatagramChannel>() {
                        @Override
                        protected void initChannel(NioDatagramChannel ch) {
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
            
            ChannelFuture future = bootstrap.bind(port).sync();
            channel = future.channel();
            log.info("UDP广播服务端启动成功,监听端口: {}", port);
            
            // 等待服务器关闭
            channel.closeFuture().sync();
            
        } catch (InterruptedException e) {
            log.error("UDP服务端异常", e);
            Thread.currentThread().interrupt();
        } finally {
            shutdown();
        }
    }
    
    /**
     * 关闭服务端
     */
    public void shutdown() {
        if (channel != null) {
            channel.close();
        }
        if (group != null) {
            group.shutdownGracefully();
        }
        log.info("UDP服务端已关闭");
    }
    
    /**
     * 服务端消息处理器
     */
    private static class ServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
            // 读取接收到的数据
            ByteBuf buf = packet.content();
            String message = buf.toString(CharsetUtil.UTF_8);
            InetSocketAddress sender = packet.sender();
            
            log.info("收到来自 {}:{} 的消息: {}", 
                    sender.getAddress().getHostAddress(), 
                    sender.getPort(), 
                    message);
            
            // 回复消息
            String response = "服务器收到: " + message + " | 时间: " + System.currentTimeMillis();
            ByteBuf responseBuf = Unpooled.copiedBuffer(response, CharsetUtil.UTF_8);
            DatagramPacket responsePacket = new DatagramPacket(responseBuf, sender);
            ctx.writeAndFlush(responsePacket).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    log.info("已回复消息给 {}:{}", sender.getAddress().getHostAddress(), sender.getPort());
                } else {
                    log.error("回复消息失败", future.cause());
                }
            });
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("UDP服务端处理异常", cause);
        }
        
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            log.info("UDP Channel 激活: {}", ctx.channel().localAddress());
        }
    }
    
    /**
     * 主方法 - 启动服务端
     */
    public static void main(String[] args) {
        UdpBroadcastServer server = new UdpBroadcastServer(8888);
        
        // 添加关闭钩子,优雅关闭
        Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown));
        
        server.start();
    }
}
// ==================== 客户端 ====================
package cn.fuzhi.service;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * UDP广播客户端测试
 * 发送广播消息并接收服务端响应
 */
@Slf4j
public class UdpBroadcastClient {
    
    private final String broadcastAddress;
    private final int serverPort;
    private EventLoopGroup group;
    private Channel channel;
    
    public UdpBroadcastClient(String broadcastAddress, int serverPort) {
        this.broadcastAddress = broadcastAddress;
        this.serverPort = serverPort;
    }
    
    /**
     * 启动客户端
     */
    public void start() {
        group = new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .option(ChannelOption.SO_RCVBUF, 2048)
                    .option(ChannelOption.SO_SNDBUF, 2048)
                    .handler(new ChannelInitializer<NioDatagramChannel>() {
                        @Override
                        protected void initChannel(NioDatagramChannel ch) {
                            ch.pipeline().addLast(new ClientHandler());
                        }
                    });
            
            // 绑定任意端口(0表示系统自动分配)
            channel = bootstrap.bind(0).sync().channel();
            log.info("UDP客户端启动成功,本地端口: {}", channel.localAddress());
            
        } catch (InterruptedException e) {
            log.error("UDP客户端启动异常", e);
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 发送广播消息
     */
    public void sendBroadcast(String message) {
        if (channel == null || !channel.isActive()) {
            log.error("Channel未激活,无法发送消息");
            return;
        }
        
        ByteBuf buf = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);
        InetSocketAddress broadcast = new InetSocketAddress(broadcastAddress, serverPort);
        DatagramPacket packet = new DatagramPacket(buf, broadcast);
        
        channel.writeAndFlush(packet).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                log.info("广播消息发送成功: {} -> {}:{}", message, broadcastAddress, serverPort);
            } else {
                log.error("广播消息发送失败", future.cause());
            }
        });
    }
    
    /**
     * 关闭客户端
     */
    public void shutdown() {
        if (channel != null) {
            channel.close();
        }
        if (group != null) {
            group.shutdownGracefully();
        }
        log.info("UDP客户端已关闭");
    }
    
    /**
     * 客户端消息处理器
     */
    private static class ClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
            ByteBuf buf = packet.content();
            String message = buf.toString(CharsetUtil.UTF_8);
            InetSocketAddress sender = packet.sender();
            
            log.info("收到来自 {}:{} 的响应: {}", 
                    sender.getAddress().getHostAddress(), 
                    sender.getPort(), 
                    message);
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("UDP客户端处理异常", cause);
        }
        
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            log.info("UDP客户端 Channel 激活: {}", ctx.channel().localAddress());
        }
    }
    
    /**
     * 主方法 - 测试客户端
     */
    public static void main(String[] args) throws InterruptedException {
        UdpBroadcastClient client = new UdpBroadcastClient("255.255.255.255", 8888);
        
        // 启动客户端
        client.start();
        
        // 等待1秒确保启动完成
        TimeUnit.SECONDS.sleep(1);
        
        // 发送多条广播消息测试
        client.sendBroadcast("DISCOVER_REQUEST");
        TimeUnit.SECONDS.sleep(2);
        
        client.sendBroadcast("PING");
        TimeUnit.SECONDS.sleep(2);
        
        client.sendBroadcast("HELLO_SERVER");
        
        // 等待10秒接收响应
        log.info("等待接收响应,10秒后自动关闭...");
        TimeUnit.SECONDS.sleep(10);
        
        // 关闭客户端
        client.shutdown();
    }
}

服务端日志:

客户端端日志:

4. 二维码 + 云端注册

流程:

  • 设备生成包含设备ID的二维码
  • 用户扫码获取设备信息
  • App通过云端API查询设备状态和连接信息
  • 建立P2P连接或通过云端中转

5. NFC近场发现

应用场景:

  • 高端穿戴设备
  • 需要物理接触的安全配对

6. 音频配网发现

声波配网:

  • 设备通过扬声器发出特定频率声波
  • 手机麦克风接收解析获得设备信息
  • 常见于智能音箱等设备

实际产品案例

小米摄像头:

  • Wi-Fi配网 + mDNS发现
  • 手机热点直连模式

Apple Watch:

  • 蓝牙配对 + 二维码辅助

海康威视摄像头:

  • SADP协议(基于UDP广播)
  • 设备发现和参数配置

大疆无人机:

  • Wi-Fi Direct连接
  • UDP广播设备信息

技术选择建议

摄像头类设备:

  • 首选:mDNS + Wi-Fi配网
  • 备选:UDP广播发现
  • 辅助:二维码配置

穿戴设备:

  • 首选:BLE广播发现
  • 辅助:NFC配对(高端设备)

智能家居设备:

  • mDNS/Bonjour(局域网内)
  • 云端注册表(跨网络访问)

这些方案通常会组合使用,比如先通过蓝牙或二维码建立初始连接,再通过Wi-Fi进行数据传输,最后通过云端服务实现远程访问。

按连接方式分类的应用场景

Wi-Fi连接设备 → mDNS/Bonjour发现

适用设备:

  • 智能摄像头(小米、海康威视、大华等)
  • 智能音箱(天猫精灵、小度、Echo)
  • 智能电视、投影仪
  • 路由器、网络存储设备

蓝牙连接设备 → BLE广播发现

适用设备:

  • 穿戴设备(Apple Watch、小米手环、华为手表)
  • 蓝牙耳机、音箱
  • 智能门锁(部分配对阶段)
  • 健康监测设备

技术特点: 蓝牙在IoT中扮演关键角色,帮助设备更容易地相互通信,使我们的家庭更智能,健康监测更高效

NFC连接设备 → NFC近场发现

适用设备:

  • 高端智能手表(Apple Watch Ultra)
  • 智能门锁(高端型号)
  • 支付设备、门禁卡
  • 智能标签

应用场景: 主要用于安全配对和快速配置

有线连接设备 → mDNS/SSDP发现

适用设备:

  • 网络摄像头(PoE供电)
  • 工业IoT设备
  • 智能网关
  • 网络存储设备

市场主流选择分析

消费级设备(占市场70%+)

  1. 智能家居设备:90%采用Wi-Fi + mDNS

    • 小米全家桶、华为HiLink、天猫精灵生态
  2. 穿戴设备:95%采用蓝牙发现

    • Apple Watch、华为GT系列、小米手环
  3. 智能音箱:混合方式

    • 初始配对:蓝牙
    • 正常使用:Wi-Fi + mDNS

企业级设备(占市场30%)

  1. 安防监控:有线网络 + ONVIF协议
  2. 工业IoT:有线为主,无线为辅
  3. 智能办公:Wi-Fi + 企业级发现协议

实际产品选择策略

按设备类型分类

固定式设备(摄像头、音箱)

Wi-Fi配网 → mDNS发现 → 云端注册
成功率:95%+

移动式设备(手表、耳机)

蓝牙配对 → 服务发现 → 数据同步
成功率:90%+

安全要求高的设备

NFC配对 → 蓝牙/Wi-Fi传输 → 云端验证
成功率:99%+

技术组合使用

典型组合1:智能摄像头

  • 配网:Wi-Fi SmartConfig
  • 发现:mDNS/Bonjour
  • 远程访问:云端P2P

典型组合2:智能手表

  • 初始配对:NFC(高端)或二维码
  • 数据传输:蓝牙BLE
  • 云端同步:Wi-Fi/蜂窝网络

典型组合3:智能门锁

  • 近场配置:NFC
  • 日常通信:蓝牙
  • 远程管理:Wi-Fi

java场景模拟

        创建一个完整的Java模拟系统,包含设备端、云端服务发现和数据传输功能。我为你创建了一个完整的IoT设备云端发现与数据传输模拟系统。这个系统包含以下核心组件:

系统架构

1. 云端服务注册中心 (CloudServiceRegistry)

  • 设备注册管理
  • 设备发现功能
  • 心跳检测机制
  • 在线状态维护

2. 云端数据服务器 (CloudDataServer)

  • 处理设备连接
  • 接收传感器数据
  • 设备注册处理
  • 服务发现响应

3. IoT设备模拟器 (IoTDeviceSimulator)

  • 模拟真实IoT设备
  • 自动注册到云端
  • 定期发送心跳
  • 传输传感器数据

主要功能特性

设备发现机制

  • 设备启动时自动注册到云端
  • 支持按设备类型过滤发现
  • 实时状态监控(在线/离线)

数据传输

  • JSON格式数据交换
  • 多种传感器数据类型
  • 异步数据处理
  • 历史数据存储

心跳监控

  • 定期心跳检测
  • 自动离线检测
  • 连接状态管理

模拟设备类型

系统预设了5种常见IoT设备:

  1. 摄像头 - 发送运动检测数据
  2. 温度传感器 - 发送温度数据
  3. 智能手表 - 发送心率数据
  4. 智能门锁 - 发送锁定状态

运行效果

系统运行时会显示:

  • 设备注册成功消息
  • 实时传感器数据接收
  • 定期设备状态列表
  • 心跳和连接状态

技术要点

  • 并发处理:使用线程池处理多设备连接
  • JSON通信:使用Gson进行数据序列化
  • 心跳机制:确保设备连接可靠性
  • 状态管理:实时监控设备在线状态

这个系统完整模拟了IoT设备从发现、注册到数据传输的全流程,是学习和理解IoT云端架构的很好示例。可以基于这个框架扩展更多功能,比如设备控制、数据分析、告警机制等。

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.text.SimpleDateFormat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

// 设备信息模型
class DeviceInfo {
    private String deviceId;
    private String deviceType;
    private String name;
    private String ipAddress;
    private int port;
    private String status;
    private long lastHeartbeat;
    private Map<String, Object> properties;
    
    public DeviceInfo(String deviceId, String deviceType, String name, String ipAddress, int port) {
        this.deviceId = deviceId;
        this.deviceType = deviceType;
        this.name = name;
        this.ipAddress = ipAddress;
        this.port = port;
        this.status = "online";
        this.lastHeartbeat = System.currentTimeMillis();
        this.properties = new HashMap<>();
    }
    
    // Getters and Setters
    public String getDeviceId() { return deviceId; }
    public String getDeviceType() { return deviceType; }
    public String getName() { return name; }
    public String getIpAddress() { return ipAddress; }
    public int getPort() { return port; }
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
    public long getLastHeartbeat() { return lastHeartbeat; }
    public void setLastHeartbeat(long lastHeartbeat) { this.lastHeartbeat = lastHeartbeat; }
    public Map<String, Object> getProperties() { return properties; }
}

// 传感器数据模型
class SensorData {
    private String deviceId;
    private String dataType;
    private Object value;
    private long timestamp;
    
    public SensorData(String deviceId, String dataType, Object value) {
        this.deviceId = deviceId;
        this.dataType = dataType;
        this.value = value;
        this.timestamp = System.currentTimeMillis();
    }
    
    // Getters
    public String getDeviceId() { return deviceId; }
    public String getDataType() { return dataType; }
    public Object getValue() { return value; }
    public long getTimestamp() { return timestamp; }
}

// 云端服务注册中心
class CloudServiceRegistry {
    private Map<String, DeviceInfo> registeredDevices;
    private ExecutorService executor;
    private Gson gson;
    private static final long HEARTBEAT_TIMEOUT = 30000; // 30秒超时
    
    public CloudServiceRegistry() {
        this.registeredDevices = new ConcurrentHashMap<>();
        this.executor = Executors.newCachedThreadPool();
        this.gson = new GsonBuilder().setPrettyPrinting().create();
        
        // 启动心跳检查定时任务
        startHeartbeatChecker();
    }
    
    // 设备注册
    public synchronized boolean registerDevice(DeviceInfo device) {
        try {
            registeredDevices.put(device.getDeviceId(), device);
            System.out.println("✅ 设备注册成功: " + device.getName() + " [" + device.getDeviceId() + "]");
            System.out.println("   类型: " + device.getDeviceType() + ", 地址: " + device.getIpAddress() + ":" + device.getPort());
            return true;
        } catch (Exception e) {
            System.err.println("❌ 设备注册失败: " + e.getMessage());
            return false;
        }
    }
    
    // 设备发现
    public List<DeviceInfo> discoverDevices(String deviceType) {
        List<DeviceInfo> devices = new ArrayList<>();
        for (DeviceInfo device : registeredDevices.values()) {
            if (deviceType == null || deviceType.equals("all") || device.getDeviceType().equals(deviceType)) {
                if ("online".equals(device.getStatus())) {
                    devices.add(device);
                }
            }
        }
        return devices;
    }
    
    // 更新设备心跳
    public void updateHeartbeat(String deviceId) {
        DeviceInfo device = registeredDevices.get(deviceId);
        if (device != null) {
            device.setLastHeartbeat(System.currentTimeMillis());
            device.setStatus("online");
        }
    }
    
    // 心跳检查器
    private void startHeartbeatChecker() {
        executor.submit(() -> {
            while (true) {
                try {
                    long currentTime = System.currentTimeMillis();
                    for (DeviceInfo device : registeredDevices.values()) {
                        if (currentTime - device.getLastHeartbeat() > HEARTBEAT_TIMEOUT) {
                            if ("online".equals(device.getStatus())) {
                                device.setStatus("offline");
                                System.out.println("⚠️  设备离线: " + device.getName() + " [" + device.getDeviceId() + "]");
                            }
                        }
                    }
                    Thread.sleep(10000); // 每10秒检查一次
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
    }
    
    // 获取设备列表
    public void printDeviceList() {
        System.out.println("\n📋 当前注册设备列表:");
        System.out.println("----------------------------------------");
        if (registeredDevices.isEmpty()) {
            System.out.println("暂无注册设备");
        } else {
            for (DeviceInfo device : registeredDevices.values()) {
                String statusIcon = "online".equals(device.getStatus()) ? "🟢" : "🔴";
                System.out.printf("%s %s [%s] - %s:%d (%s)\n", 
                    statusIcon, device.getName(), device.getDeviceId(), 
                    device.getIpAddress(), device.getPort(), device.getDeviceType());
            }
        }
        System.out.println("----------------------------------------\n");
    }
}

// 云端数据服务器
class CloudDataServer {
    private int port;
    private ServerSocket serverSocket;
    private CloudServiceRegistry registry;
    private ExecutorService executor;
    private List<SensorData> dataHistory;
    private Gson gson;
    
    public CloudDataServer(int port, CloudServiceRegistry registry) {
        this.port = port;
        this.registry = registry;
        this.executor = Executors.newCachedThreadPool();
        this.dataHistory = Collections.synchronizedList(new ArrayList<>());
        this.gson = new GsonBuilder().setPrettyPrinting().create();
    }
    
    public void start() {
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("☁️  云端数据服务器启动,监听端口: " + port);
            
            while (!serverSocket.isClosed()) {
                Socket clientSocket = serverSocket.accept();
                executor.submit(new ClientHandler(clientSocket));
            }
        } catch (IOException e) {
            if (!serverSocket.isClosed()) {
                System.err.println("❌ 云端服务器错误: " + e.getMessage());
            }
        }
    }
    
    private class ClientHandler implements Runnable {
        private Socket socket;
        private BufferedReader reader;
        private PrintWriter writer;
        
        public ClientHandler(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            try {
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                writer = new PrintWriter(socket.getOutputStream(), true);
                
                String message;
                while ((message = reader.readLine()) != null) {
                    handleMessage(message);
                }
            } catch (IOException e) {
                System.err.println("客户端连接异常: " + e.getMessage());
            } finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        
        private void handleMessage(String message) {
            try {
                Map<String, Object> data = gson.fromJson(message, Map.class);
                String type = (String) data.get("type");
                
                switch (type) {
                    case "register":
                        handleDeviceRegistration(data);
                        break;
                    case "heartbeat":
                        handleHeartbeat(data);
                        break;
                    case "data":
                        handleSensorData(data);
                        break;
                    case "discovery":
                        handleDeviceDiscovery(data);
                        break;
                    default:
                        writer.println(gson.toJson(Map.of("status", "error", "message", "Unknown message type")));
                }
            } catch (Exception e) {
                writer.println(gson.toJson(Map.of("status", "error", "message", e.getMessage())));
            }
        }
        
        private void handleDeviceRegistration(Map<String, Object> data) {
            String deviceId = (String) data.get("deviceId");
            String deviceType = (String) data.get("deviceType");
            String name = (String) data.get("name");
            String ipAddress = socket.getInetAddress().getHostAddress();
            int port = ((Double) data.get("port")).intValue();
            
            DeviceInfo device = new DeviceInfo(deviceId, deviceType, name, ipAddress, port);
            boolean success = registry.registerDevice(device);
            
            Map<String, Object> response = new HashMap<>();
            response.put("status", success ? "success" : "error");
            response.put("message", success ? "Device registered successfully" : "Registration failed");
            writer.println(gson.toJson(response));
        }
        
        private void handleHeartbeat(Map<String, Object> data) {
            String deviceId = (String) data.get("deviceId");
            registry.updateHeartbeat(deviceId);
            writer.println(gson.toJson(Map.of("status", "ok")));
        }
        
        private void handleSensorData(Map<String, Object> data) {
            String deviceId = (String) data.get("deviceId");
            String dataType = (String) data.get("dataType");
            Object value = data.get("value");
            
            SensorData sensorData = new SensorData(deviceId, dataType, value);
            dataHistory.add(sensorData);
            
            // 限制历史数据数量
            if (dataHistory.size() > 1000) {
                dataHistory.remove(0);
            }
            
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            System.out.println("📊 收到数据: [" + deviceId + "] " + dataType + " = " + value + 
                             " (时间: " + sdf.format(new Date(sensorData.getTimestamp())) + ")");
            
            writer.println(gson.toJson(Map.of("status", "received")));
        }
        
        private void handleDeviceDiscovery(Map<String, Object> data) {
            String deviceType = (String) data.get("deviceType");
            List<DeviceInfo> devices = registry.discoverDevices(deviceType);
            
            Map<String, Object> response = new HashMap<>();
            response.put("status", "success");
            response.put("devices", devices);
            writer.println(gson.toJson(response));
        }
    }
    
    public void stop() {
        try {
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close();
            }
            executor.shutdown();
        } catch (IOException e) {
            System.err.println("停止服务器时出错: " + e.getMessage());
        }
    }
}

// IoT设备模拟器
class IoTDeviceSimulator {
    private String deviceId;
    private String deviceType;
    private String name;
    private String cloudServerHost;
    private int cloudServerPort;
    private int localPort;
    private Socket cloudSocket;
    private PrintWriter cloudWriter;
    private BufferedReader cloudReader;
    private ScheduledExecutorService scheduler;
    private Random random;
    private Gson gson;
    private boolean isConnected;
    
    public IoTDeviceSimulator(String deviceId, String deviceType, String name, 
                             String cloudServerHost, int cloudServerPort, int localPort) {
        this.deviceId = deviceId;
        this.deviceType = deviceType;
        this.name = name;
        this.cloudServerHost = cloudServerHost;
        this.cloudServerPort = cloudServerPort;
        this.localPort = localPort;
        this.scheduler = Executors.newScheduledThreadPool(2);
        this.random = new Random();
        this.gson = new GsonBuilder().setPrettyPrinting().create();
        this.isConnected = false;
    }
    
    public void start() {
        try {
            // 连接到云端服务器
            connectToCloud();
            
            // 注册设备
            registerToCloud();
            
            // 启动心跳
            startHeartbeat();
            
            // 启动数据发送
            startDataTransmission();
            
            System.out.println("🔌 设备启动成功: " + name + " [" + deviceId + "]");
            
        } catch (Exception e) {
            System.err.println("❌ 设备启动失败: " + e.getMessage());
        }
    }
    
    private void connectToCloud() throws IOException {
        cloudSocket = new Socket(cloudServerHost, cloudServerPort);
        cloudWriter = new PrintWriter(cloudSocket.getOutputStream(), true);
        cloudReader = new BufferedReader(new InputStreamReader(cloudSocket.getInputStream()));
        isConnected = true;
    }
    
    private void registerToCloud() throws IOException {
        Map<String, Object> registerData = new HashMap<>();
        registerData.put("type", "register");
        registerData.put("deviceId", deviceId);
        registerData.put("deviceType", deviceType);
        registerData.put("name", name);
        registerData.put("port", localPort);
        
        cloudWriter.println(gson.toJson(registerData));
        String response = cloudReader.readLine();
        System.out.println("📝 注册响应: " + response);
    }
    
    private void startHeartbeat() {
        scheduler.scheduleAtFixedRate(() -> {
            if (isConnected) {
                try {
                    Map<String, Object> heartbeatData = new HashMap<>();
                    heartbeatData.put("type", "heartbeat");
                    heartbeatData.put("deviceId", deviceId);
                    
                    cloudWriter.println(gson.toJson(heartbeatData));
                    cloudReader.readLine(); // 读取响应
                } catch (Exception e) {
                    System.err.println("💓 心跳发送失败: " + e.getMessage());
                    isConnected = false;
                }
            }
        }, 5, 10, TimeUnit.SECONDS);
    }
    
    private void startDataTransmission() {
        scheduler.scheduleAtFixedRate(() -> {
            if (isConnected) {
                try {
                    sendSensorData();
                } catch (Exception e) {
                    System.err.println("📡 数据发送失败: " + e.getMessage());
                }
            }
        }, 2, 5, TimeUnit.SECONDS);
    }
    
    private void sendSensorData() throws IOException {
        Map<String, Object> sensorData = new HashMap<>();
        sensorData.put("type", "data");
        sensorData.put("deviceId", deviceId);
        
        // 根据设备类型生成不同的传感器数据
        switch (deviceType) {
            case "camera":
                sensorData.put("dataType", "motion_detected");
                sensorData.put("value", random.nextBoolean());
                break;
            case "temperature_sensor":
                sensorData.put("dataType", "temperature");
                sensorData.put("value", 20 + random.nextDouble() * 15); // 20-35度
                break;
            case "smart_watch":
                sensorData.put("dataType", "heart_rate");
                sensorData.put("value", 60 + random.nextInt(40)); // 60-100 BPM
                break;
            case "smart_lock":
                sensorData.put("dataType", "lock_status");
                sensorData.put("value", random.nextBoolean() ? "locked" : "unlocked");
                break;
            default:
                sensorData.put("dataType", "status");
                sensorData.put("value", "active");
        }
        
        cloudWriter.println(gson.toJson(sensorData));
        cloudReader.readLine(); // 读取响应
    }
    
    public void stop() {
        try {
            isConnected = false;
            scheduler.shutdown();
            if (cloudSocket != null && !cloudSocket.isClosed()) {
                cloudSocket.close();
            }
            System.out.println("🔌 设备已停止: " + name);
        } catch (IOException e) {
            System.err.println("停止设备时出错: " + e.getMessage());
        }
    }
}

// 主程序
public class IoTCloudSystem {
    public static void main(String[] args) {
        System.out.println("🌐 IoT设备云端发现与数据传输模拟系统");
        System.out.println("=====================================");
        
        // 创建云端服务注册中心
        CloudServiceRegistry registry = new CloudServiceRegistry();
        
        // 创建云端数据服务器
        CloudDataServer cloudServer = new CloudDataServer(8080, registry);
        
        // 在新线程中启动云端服务器
        new Thread(() -> cloudServer.start()).start();
        
        // 等待服务器启动
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // 创建并启动IoT设备模拟器
        List<IoTDeviceSimulator> devices = new ArrayList<>();
        
        // 摄像头设备
        devices.add(new IoTDeviceSimulator("CAM_001", "camera", "客厅摄像头", "localhost", 8080, 9001));
        devices.add(new IoTDeviceSimulator("CAM_002", "camera", "门口摄像头", "localhost", 8080, 9002));
        
        // 温度传感器
        devices.add(new IoTDeviceSimulator("TEMP_001", "temperature_sensor", "卧室温度传感器", "localhost", 8080, 9003));
        
        // 智能手表
        devices.add(new IoTDeviceSimulator("WATCH_001", "smart_watch", "小明的智能手表", "localhost", 8080, 9004));
        
        // 智能门锁
        devices.add(new IoTDeviceSimulator("LOCK_001", "smart_lock", "前门智能门锁", "localhost", 8080, 9005));
        
        // 启动所有设备
        for (IoTDeviceSimulator device : devices) {
            device.start();
            try {
                Thread.sleep(1000); // 间隔启动
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        // 定期显示设备列表
        ScheduledExecutorService statusScheduler = Executors.newScheduledThreadPool(1);
        statusScheduler.scheduleAtFixedRate(() -> {
            registry.printDeviceList();
        }, 10, 15, TimeUnit.SECONDS);
        
        // 运行30秒后停止
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        System.out.println("\n🛑 正在停止系统...");
        
        // 停止所有设备
        for (IoTDeviceSimulator device : devices) {
            device.stop();
        }
        
        // 停止服务器
        cloudServer.stop();
        statusScheduler.shutdown();
        
        System.out.println("✅ 系统已停止");
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱钓鱼的程序员小郭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值