Golang通过Socket发送二进制数据包
Golang通过Socket发送二进制数据包 最近开始学习Go语言,作为学习的一部分,我想实现一个与GPS设备通信的小型网络服务器。
协议非常简单:设备首先发送其标识符,如果服务器希望接收该设备的数据,则应回复01,否则回复00。响应应以二进制数据包形式发送。之前在Python中,这是通过发送bytes值b'\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
不,你不需要那样做。错误返回信息说了什么?
更多关于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() 被调用但没有收到数据后,连接立即关闭了。
我的理解是否正确:要等待数据,我需要采用另一种方法,比如:
- 启动一个 goroutine,在一个无限循环中接受连接并将它们存储到通道中
- 在连接处理程序中,启动一个带有无限循环的 goroutine,该循环将等待来自套接字的数据并将它们发送到另一个通道
- 如果接收到数据——处理它们,必要时发送响应,然后再次等待数据或关闭连接
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)验证数据流。

