Golang通过Socket发送二进制数据包

Golang通过Socket发送二进制数据包 最近开始学习Go语言,作为学习的一部分,我想实现一个与GPS设备通信的小型网络服务器。

协议非常简单:设备首先发送其标识符,如果服务器希望接收该设备的数据,则应回复01,否则回复00。响应应以二进制数据包形式发送。之前在Python中,这是通过发送bytesb'\x01'b'\x00'完成的。

现在我卡住了。使用以下代码,我可以成功接收带有设备ID的初始数据包,但找不到正确发送响应的方法。结果,第二个数据包始终为空,设备不发送任何数据。

func handleConnection(conn net.Conn) {
	remoteAddr := conn.RemoteAddr().String()
	fmt.Println("Client connected:", remoteAddr)

    buf := make([]byte, 1024)

    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("Read error", err.Error())
        conn.Close()
    }

    fmt.Println("Received bytes:", n)
    fmt.Println("ID", string(buf[2:17]))

    err = binary.Write(conn, binary.BigEndian, a)
    if err != nil {
        fmt.Println("Write error", err.Error())
        conn.Close()
    }

    n, err = conn.Read(buf)
    if err != nil {
        fmt.Println("Read error", err.Error())
        conn.Close()
    }

    fmt.Println("Received bytes:", n)
    fmt.Println("Data", string(buf[:n]))

我还尝试以以下方式发送数据:

_, err = conn.Write([]byte{0x01})

var a byte = 1
t := new(bytes.Buffer)
err = binary.Write(t, binary.BigEndian, a)
if err != nil {
    fmt.Println("binary.Write failed:", err)
}
_, err = conn.Write(t.Bytes())

我是不是做错了什么?

谢谢,抱歉我的英语不好。


更多关于Golang通过Socket发送二进制数据包的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

不,你不需要那样做。错误返回信息说了什么?

更多关于Golang通过Socket发送二进制数据包的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Read() 方法不一定会一次性返回你期望的所有数据。可以查看 io.ReadFull 的相关说明。

这很奇怪。在 Go 中读取 TCP net.Conn 不会重复返回零字节且无错误的情况。(除非缓冲区大小为零,但您上面的代码中并非如此。)

你的写法没有错。(与简单的 conn.Write([]byte{1}) 相比,使用 binary.BigEndian 和 buffer 的方式显得不必要的复杂,但在我看来也没有明显的错误。)

func main() {
    fmt.Println("hello world")
}

既然你提到"数据包",这是UDP协议吗?如果是这种情况,我猜你从另一端收到了一个空数据包。请重新读取。

如果不是这种情况,可能是对方关闭了连接的写入端。请重新读取,你可能会看到EOF错误。

不,这是TCP协议。我所说的数据包是指设备发送的数据。

我也用netcat进行了测试,它在接收到服务器响应后就断开了连接。再添加一个Read()调用也没有帮助,它同样读取到零字节且不会等待任何数据到达。

Read()

谢谢!我花了更多时间调试代码和检查服务器与客户端之间的通信,看起来第一部分工作正常:服务器接收来自客户端的数据并发送正确的响应。但无法接收到包含实际数据的第二个数据包。

第二次调用 n, err = conn.Read(buf)(在向客户端发送确认后)显示接收到了零字节,数据包为空。这是预期的吗?可能我需要以某种方式"重置"套接字才能读取来自客户端的第二个数据包?

n, err = conn.Read(buf)

没有错误,我收到以下调试信息:

Starting server...
Listening at 127.0.0.1:9999
Client connected: 127.0.0.1:40014
Receiving device ID...
Error <nil>
Received bytes 18
Packet content [49 49 51 53 49 53 49 51 48 53 50 49 56 54 52 49 53 10]
Sending responce...
Error <nil>
Sent bytes 1
Receiving data...
Error <nil>
Received bytes 0
Packet content []

可能我之前没有表达清楚。问题是当我使用 conn.Write([]byte{1}) 写入单个字节后,后续调用 conn.Read() 返回了零字节。给我的感觉是它没有等待数据到达,而且在上次 Read() 被调用但没有收到数据后,连接立即关闭了。

我的理解是否正确:要等待数据,我需要采用另一种方法,比如:

  1. 启动一个 goroutine,在一个无限循环中接受连接并将它们存储到通道中
  2. 在连接处理程序中,启动一个带有无限循环的 goroutine,该循环将等待来自套接字的数据并将它们发送到另一个通道
  3. 如果接收到数据——处理它们,必要时发送响应,然后再次等待数据或关闭连接
conn.Write([]byte{1})

在您的代码中,问题可能在于设备期望的响应格式或网络缓冲处理。以下是修复后的示例代码,使用conn.Write([]byte{0x01})直接发送二进制响应,这是正确的方法:

package main

import (
    "fmt"
    "net"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()
    remoteAddr := conn.RemoteAddr().String()
    fmt.Println("Client connected:", remoteAddr)

    buf := make([]byte, 1024)

    // 读取设备标识符
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Println("Read error:", err)
        return
    }

    fmt.Println("Received bytes:", n)
    deviceID := string(buf[2:17]) // 根据协议调整索引
    fmt.Println("ID:", deviceID)

    // 发送二进制响应:01 表示接受,00 表示拒绝
    // 这里假设服务器总是回复 01 来接收数据
    response := []byte{0x01}
    _, err = conn.Write(response)
    if err != nil {
        fmt.Println("Write error:", err)
        return
    }

    // 读取设备发送的数据
    n, err = conn.Read(buf)
    if err != nil {
        fmt.Println("Read error:", err)
        return
    }

    fmt.Println("Received bytes:", n)
    fmt.Println("Data:", string(buf[:n]))
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close()
    fmt.Println("Server listening on :8080")

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            continue
        }
        go handleConnection(conn)
    }
}

关键点:

  • 使用conn.Write([]byte{0x01})直接发送单字节二进制数据,无需通过binary.Write
  • 添加defer conn.Close()确保连接关闭。
  • 根据协议,设备标识符的解析可能需要调整索引(例如buf[2:17]),请根据实际数据格式修改。

您的第二种方法(使用conn.Write([]byte{0x01}))是正确的,问题可能在于设备协议的其他细节或网络延迟。确保设备在发送标识符后等待响应,并且服务器及时发送回复。如果设备仍无响应,检查网络抓包工具(如Wireshark)验证数据流。

回到顶部