【发布时间】:2020-09-03 04:48:21
【问题描述】:
我正在开发一个个人项目,该项目将在 Raspberry Pi 上运行,并附有一些传感器。
它由两个程序组成:
- 每 X 秒从传感器读取数据的服务器
- 在 sqlite 数据库中保存数据并可以发送一些命令的客户端
服务器可以:
- 从传感器读取数据,将它们写入套接字,以便客户端可以将其保存在数据库中
- 监听套接字,这样当客户端发送一些命令时,它可以执行它并将响应发送回客户端
从传感器读取的函数和处理套接字连接的函数在不同的 goroutine 中执行,因此,为了在从传感器读取数据时在套接字上发送数据,我在其中创建了一个 []byte 通道main 函数,将其传递给 goroutines。
当从传感器收集数据时(如果有客户端连接)被写入通道,所以另一个函数将它写入套接字并且客户端接收它。
我的问题出现在这里:如果我连续执行多次写入,则只有第一个数据到达客户端,而其他数据不会。但是,如果我在写入通道的函数中添加一点 time.sleep,所有数据都会正确到达客户端。
不管怎样,这是这个小程序的简化版:
package main
import (
"net"
"os"
"sync"
"time"
)
const socketName string = "./test_socket"
// create to the socket and launch the accept client routine
func launchServerUDS(ch chan []byte) {
if err := os.RemoveAll(socketName); err != nil {
return
}
l, err := net.Listen("unix", socketName)
if err != nil {
return
}
go acceptConnectionRoutine(l, ch)
}
// accept incoming connection on the socket and
// 1) launch the routine to handle commands from the client
// 2) launch the routine to send data when the server reads from the sensors
func acceptConnectionRoutine(l net.Listener, ch chan []byte) {
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
return
}
go commandsHandlerRoutine(conn, ch)
go autoSendRoutine(conn, ch)
}
}
// routine that sends data to the client
func autoSendRoutine(c net.Conn, ch chan []byte) {
for {
data := <-ch
if string(data) == "exit" {
return
}
c.Write(data)
}
}
// handle client connection and calls functions to execute commands
func commandsHandlerRoutine(c net.Conn, ch chan []byte) {
for {
buf := make([]byte, 1024)
n, err := c.Read(buf)
if err != nil {
ch <- []byte("exit")
break
}
// now, for sake of simplicity , only echo commands back to the client
_, err = c.Write(buf[:n])
if err != nil {
ch <- []byte("exit")
break
}
}
}
// write on the channel to the autosend routine so the data are written on the socket
func sendDataToClient(data []byte, ch chan []byte) {
select {
case ch <- data:
// if i put a little sleep here, no problems
// i i remove the sleep, only data1 is sent to the client
// time.Sleep(1 * time.Millisecond)
default:
}
}
func dummyReadDataRoutine(ch chan []byte) {
for {
// read data from the sensors every 5 seconds
time.Sleep(5 * time.Second)
// read first data and send it
sendDataToClient([]byte("dummy data1\n"), ch)
// read second data and send it
sendDataToClient([]byte("dummy data2\n"), ch)
// read third data and send it
sendDataToClient([]byte("dummy data3\n"), ch)
}
}
func main() {
ch := make(chan []byte)
wg := sync.WaitGroup{}
wg.Add(2)
go dummyReadDataRoutine(ch)
go launchServerUDS(ch)
wg.Wait()
}
我认为我错过了一些东西,我不想用睡眠来写作,因为我认为这样做不是正确的方法。是否有一些错误,或者更好的方法来做到这一点?唯一必须像我一样保持的是套接字处理和读取数据函数必须在不同的 goroutine 中执行。
【问题讨论】:
-
你可能没有找到任何搜索,因为你有一堆不相关的问题。始终尝试尽可能地简化来创建您的minimal reproducible example,因为这些都与unix 套接字无关。你有 goroutines 调用 goroutines 无缘无故调用 goroutines。你的
WaitGroup什么都不做,因为没有 goroutine 调用Done()(他们也不能,因为它不在他们的范围内)。通过通道发送数据不必要地在一个单独的函数中,在您选择default的情况下没有明显的原因。 -
通道是无缓冲的,你用一个选择和一个不做任何事情的默认值写入通道。要修复您的代码,您应该向通道添加缓冲并删除选择。然后只需将数据写入通道。
-
@JimB 嗨,谢谢你的建议,这个例子对我来说是简化的,因为我的程序比这个大,我在这里添加了等待组只是为了启动例程,所以我没有放无论如何,任何 wg.Done() 出于这个原因,非常感谢您的建议
-
@chmike 我会尝试你的解决方案并编辑我的问题,也谢谢你
-
@Leonardo,留下这样无意义的代码意味着我们无法区分留下不完整的内容和您不理解的内容。
default的情况会丢失发送数据,但这显然是错误的(就像额外的 goroutine 或未使用的等待组显然是错误的一样),所以我们首先需要上下文来了解它为什么存在。
标签: sockets go concurrency channels writing