Я работаю над личным проектом, который будет работать на Raspberry Pi с подключенными к нему датчиками.
Он состоит из 2-х программ:
- сервер, который считывает данные с датчиков каждые X секунд
- клиент, который сохраняет данные в базе данных sqlite и может отправлять некоторые команды
Сервер может:
- считывать данные с датчиков, записывать их в сокет, чтобы клиент мог сохранить их в БД
- слушать сокет, чтобы, когда клиент отправляет какую-либо команду, он может ее выполнить и отправить ответ обратно клиенту
Функция, считывающая с датчиков, и функция, которая обрабатывает соединение с сокетом, выполняются в разных горутиках, поэтому, чтобы отправлять данные в сокет, когда они считываются с датчиков, я создаю байтовый канал [] в основной функции , передав его в горутины.
Когда данные собираются с датчиков (если есть подключенный клиент), они записываются в канал, поэтому другая функция записывает их в сокет, и клиент получает их.
Моя проблема возникла здесь: если я делаю несколько записей подряд, только первые данные поступают к клиенту, а другие - нет. Но если я добавлю немного 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()
}
Я думаю, что мне что-то не хватает, и я не хочу использовать сон, чтобы писать, потому что я не думаю, что это правильный способ делать это. Есть ли какая-то ошибка или лучший способ сделать это? Единственное, что должно остаться, как и я, - это то, что функции обработки сокетов и чтения данных должны выполняться в разных горутинах.
WaitGroup
ничего не делает, потому что ни одна горутина не вызываетDone()
(и они не могли, потому что это не входит в их область действия). Отправка данных по каналу без необходимости находится в отдельной функции сdefault
регистром в вашем выборе без видимой причины. - person JimB   schedule 17.05.2020default
- это то, что потеряло бы отправку данных, но это, очевидно, неверно (точно так же, как дополнительные горутины или неиспользуемая группа ожидания, очевидно, неверны), поэтому нам нужен контекст, чтобы понять, почему он там вообще. - person JimB   schedule 17.05.2020Read
, см.io.Reader
. Также сомнительно, что нет кадрирования сообщений, поскольку сокеты unix представляют собой поток, вы можете получать несколько или частичные сообщения при каждом чтении или записи. Вы также обнаружите, что большинство серверов будут изо всех сил стараться избегать выделения нового буфера для каждого отдельного чтения, но это означает реструктуризацию вашей программы в целом (хотя это не вызывает особого беспокойства, если чтения выполняются нечасто). - person JimB   schedule 17.05.2020