You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于自定义TCP协议的网络设备通信开发实现求助

Hey there! Let's break down how to implement this TCP communication protocol step by step—since you're familiar with JavaScript (Node.js, since browser JS can't handle raw TCP connections) and Ruby, I'll cover both languages to help you get up and running with the data string收发逻辑 you're stuck on.

First, Let's Recap the Protocol Structure

Let's restate the device's TCP protocol clearly to eliminate any ambiguity:

  • Command Header: Fixed 1-byte value 0x7E
  • Data Length: 4-byte integer (most embedded devices use big-endian/network byte order, but we'll cover adjusting for little-endian if needed)
  • Command Type: 1-byte value (ranges from 0x01 to 0xFF)
  • Data String: Variable-length string (matches the Data Length value, uses URL-encoded key-value pairs as shown in examples)
  • Command Footer: Fixed 1-byte value 0x7F

Step 1: Building & Sending TCP Packets

First, we need to assemble the bytes in the correct order for any command you want to send. Let's use your two examples to demonstrate.

Example 1: Request Device Info (Command 0x01)

No data is sent, so Data Length = 0.

JavaScript (Node.js) Implementation

const net = require('net');

// Helper to build valid TCP packets
function buildPacket(commandType, dataStr = '') {
  const header = Buffer.from([0x7E]);
  const dataLength = Buffer.alloc(4);
  // Write 4-byte big-endian integer (use writeUInt32LE for little-endian if needed)
  dataLength.writeUInt32BE(dataStr.length, 0);
  const command = Buffer.from([commandType]);
  const data = Buffer.from(dataStr, 'utf8');
  const footer = Buffer.from([0x7F]);

  // Combine all parts into a single buffer
  return Buffer.concat([header, dataLength, command, data, footer]);
}

// Connect to the device and send the request
const client = new net.Socket();
client.connect(/* replace with device port */, /* replace with device IP */, () => {
  console.log('Connected to device');
  const deviceInfoPacket = buildPacket(0x01); // Command 0x01, no data
  client.write(deviceInfoPacket);
});

Ruby Implementation

require 'socket'

# Helper to build valid TCP packets
def build_packet(command_type, data_str = '')
  header = [0x7E].pack('C') # C = unsigned 1-byte char
  data_length = [data_str.length].pack('N') # N = big-endian 4-byte integer (use 'V' for little-endian)
  command = [command_type].pack('C')
  data = data_str.encode('utf-8')
  footer = [0x7F].pack('C')

  # Combine all parts
  header + data_length + command + data + footer
end

# Connect to the device and send the request
socket = TCPSocket.new(/* replace with device IP */, /* replace with device port */)
puts "Connected to device"
device_info_packet = build_packet(0x01)
socket.write(device_info_packet)

Example 2: Set Brightness to 50 (Command 0x0F)

Data string is "Luminance=50", so Data Length = 10 (strlen of the string). Just call the helper function with the command type and data:

  • JS: buildPacket(0x0F, "Luminance=50")
  • Ruby: build_packet(0x0F, "Luminance=50")

Step 2: Parsing Received Packets

Receiving data is trickier because TCP is a stream protocol—you might get partial packets or multiple packets at once. We need to buffer incoming data and parse it only when we have a complete, valid packet.

Key Parsing Steps:

  1. Buffer all incoming data.
  2. Check if the buffer starts with the valid header 0x7E.
  3. Extract the 4-byte Data Length to calculate the total packet size.
  4. Wait until the buffer has enough bytes for the full packet.
  5. Extract command type, data string, and verify the footer is 0x7F.
  6. Process the packet, then remove it from the buffer and continue parsing remaining data.

JavaScript (Node.js) Implementation

let buffer = Buffer.alloc(0);

client.on('data', (data) => {
  buffer = Buffer.concat([buffer, data]);

  // Keep parsing while there's enough data for a full packet
  while (buffer.length >= 7) { // Minimum packet size: 1+4+1+1 = 7 bytes
    // Skip invalid headers
    if (buffer[0] !== 0x7E) {
      buffer = buffer.slice(1);
      continue;
    }

    // Extract data length (big-endian)
    const dataLength = buffer.readUInt32BE(1);
    const totalPacketSize = 1 + 4 + 1 + dataLength + 1; // Header + Length + Command + Data + Footer

    if (buffer.length < totalPacketSize) {
      // Not enough data yet, wait for more
      break;
    }

    // Extract packet components
    const commandType = buffer[5];
    const dataStr = buffer.toString('utf8', 6, 6 + dataLength);
    const footer = buffer[6 + dataLength];

    // Validate footer
    if (footer !== 0x7F) {
      console.error('Invalid packet footer received');
      buffer = buffer.slice(totalPacketSize);
      continue;
    }

    // Process the parsed packet
    processReceivedPacket(commandType, dataStr);

    // Remove processed packet from buffer
    buffer = buffer.slice(totalPacketSize);
  }
});

// Handle different response types
function processReceivedPacket(commandType, dataStr) {
  switch(commandType) {
    case 0x02: // Device info response
      const deviceInfo = new URLSearchParams(dataStr);
      console.log('Device Info:', {
        deviceId: deviceInfo.get('DeviceIdData'),
        version: deviceInfo.get('Version'),
        deviceMac: deviceInfo.get('DeviceMacAdd'),
        // Add other fields as needed
      });
      break;
    case 0x10: // Brightness response (0x10 = 16 in decimal)
      const brightnessData = new URLSearchParams(dataStr);
      console.log('Current Brightness:', brightnessData.get('luminance'));
      break;
    default:
      console.log(`Unknown command type received: 0x${commandType.toString(16)}`);
  }
}

Ruby Implementation

buffer = ''

socket = TCPSocket.new(/* replace with device IP */, /* replace with device port */)
loop do
  data = socket.readpartial(1024)
  buffer += data

  while buffer.length >= 7
    # Skip invalid headers
    unless buffer[0] == 0x7E.chr
      buffer = buffer[1..-1]
      next
    end

    # Extract data length (big-endian)
    data_length = buffer[1..4].unpack('N')[0]
    total_packet_size = 1 + 4 + 1 + data_length + 1

    if buffer.length < total_packet_size
      break
    end

    # Extract packet components
    command_type = buffer[5].ord
    data_str = buffer[6...(6 + data_length)]
    footer = buffer[(6 + data_length)].ord

    # Validate footer
    if footer != 0x7F
      puts 'Invalid packet footer received'
      buffer = buffer[total_packet_size..-1]
      next
    end

    # Process the parsed packet
    process_received_packet(command_type, data_str)

    # Remove processed packet from buffer
    buffer = buffer[total_packet_size..-1]
  end
end

# Handle different response types
def process_received_packet(command_type, data_str)
  case command_type
  when 0x02 # Device info response
    device_info = Hash[URI::decode_www_form(data_str)]
    puts "Device Info:"
    puts "  Device ID: #{device_info['DeviceIdData']}"
    puts "  Firmware Version: #{device_info['Version']}"
    puts "  MAC Address: #{device_info['DeviceMacAdd']}"
    # Add other fields as needed
  when 0x10 # Brightness response
    brightness_data = Hash[URI::decode_www_form(data_str)]
    puts "Current Brightness: #{brightness_data['luminance']}"
  else
    puts "Unknown command type received: 0x#{command_type.to_s(16)}"
  end
end

Important Tips to Avoid Headaches

  • Endianness Check: If you're getting garbage data or invalid responses, test switching to little-endian (in JS use readUInt32LE/writeUInt32LE; in Ruby use 'V' instead of 'N' for packing/unpacking).
  • Error Handling: Expand the examples with connection timeout, disconnection, and invalid packet handling for production use.
  • Web App Integration: Browser JS can't make raw TCP connections, so if you need this for a web app, build a Node.js/Ruby backend that handles TCP communication and exposes an API to your frontend.

内容的提问来源于stack exchange,提问作者Scotty Bollinger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 18:09:07