Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sockets 通道的golang问题(缓冲与非缓冲)_Sockets_Go_Concurrency_Channels_Writing - Fatal编程技术网

Sockets 通道的golang问题(缓冲与非缓冲)

Sockets 通道的golang问题(缓冲与非缓冲),sockets,go,concurrency,channels,writing,Sockets,Go,Concurrency,Channels,Writing,我正在做一个个人项目,它将运行在一个树莓圆周率与一些传感器连接到它 它由两个程序组成: 每X秒从传感器读取数据的服务器 在sqlite数据库上保存数据并可以发送一些命令的客户端 服务器可以: 从传感器读取数据,将其写入套接字,以便客户端可以将其保存在数据库中 监听套接字,这样当客户端发送一些命令时,它就可以执行它并将响应发送回客户端 从传感器读取的函数和处理套接字连接的函数在不同的goroutine中执行,因此,为了在从传感器读取数据时在套接字上发送数据,我在main函数中创建了一个[]字节

我正在做一个个人项目,它将运行在一个树莓圆周率与一些传感器连接到它

它由两个程序组成:

  • 每X秒从传感器读取数据的服务器
  • 在sqlite数据库上保存数据并可以发送一些命令的客户端
  • 服务器可以:

    • 从传感器读取数据,将其写入套接字,以便客户端可以将其保存在数据库中
    • 监听套接字,这样当客户端发送一些命令时,它就可以执行它并将响应发送回客户端
    从传感器读取的函数和处理套接字连接的函数在不同的goroutine中执行,因此,为了在从传感器读取数据时在套接字上发送数据,我在main函数中创建了一个[]字节通道,将其传递给goroutine

    当从传感器(如果连接了客户端)收集数据时,会将数据写入通道,因此另一个函数会将数据写入套接字,客户端会接收数据

    我的问题出现在这里:如果我在一行中进行多次写入,只有第一个数据到达客户机,而其他数据没有。但是如果我在写通道的函数中加上一点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()
    }
    
    
    主程序包
    进口(
    “净额”
    “操作系统”
    “同步”
    “时间”
    )
    const socketName string=“./test_socket”
    //创建套接字并启动接受客户端例程
    func launchServerUDS(ch chan[]字节){
    如果错误:=os.RemoveAll(socketName);错误!=nil{
    返回
    }
    l、 err:=net.Listen(“unix”,socketName)
    如果错误!=零{
    返回
    }
    go AcceptConnection例行程序(l、ch)
    }
    //接受套接字上的传入连接,然后
    //1)启动例程以处理来自客户端的命令
    //2)当服务器从传感器读取数据时,启动例程发送数据
    func acceptConnectionRoutine(l net.Listener,ch chan[]字节){
    延迟l.关闭()
    为了{
    conn,err:=l.Accept()
    如果错误!=零{
    返回
    }
    go commandsHandlerRoutine(康涅狄格州,ch)
    自动结束例行程序(连接,通道)
    }
    }
    //向客户端发送数据的例程
    func自动结束例程(c网络连接,通道[]字节){
    为了{
    
    资料:=JimB给出了一个很好的解释,所以我认为他的答案更好

    我在这个答案中包含了我的部分解决方案

    我认为我的代码是清晰和简化的,但正如Jim所说,我可以做得更简单和更清晰。我将我的旧代码发布,以便人们能够更好地理解如何发布更简单的代码,而不是像我那样做得一团糟

    正如chmike所说,我的问题与我所想的套接字无关,只是与通道有关。在无缓冲通道上写入是问题之一。将无缓冲通道更改为缓冲通道后,问题得到了解决。无论如何,此代码不是“好代码”并且可以按照JimB在回答中所写的原则进行改进

    下面是新代码:

    package main
    
    import (
        "net"
        "os"
        "sync"
        "time"
    )
    
    const socketName string = "./test_socket"
    
    // create the socket and accept clients connections
    func launchServerUDS(ch chan []byte, wg *sync.WaitGroup) {
        defer wg.Done()
        if err := os.RemoveAll(socketName); err != nil {
            return
        }
        l, err := net.Listen("unix", socketName)
        if err != nil {
            return
        }
        defer l.Close()
        for {
            conn, err := l.Accept()
            if err != nil {
                return
            }
            // this goroutine are launched when a client is connected
            // routine that listen and echo commands
            go commandsHandlerRoutine(conn, ch)
            // routine to send data read from the sensors to the client
            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 commands received from the client
    func commandsHandlerRoutine(c net.Conn, ch chan []byte) {
        for {
            buf := make([]byte, 1024)
            n, err := c.Read(buf)
            if err != nil {
                // if i can't read send an exit command to autoSendRoutine and exit
                ch <- []byte("exit")
                break
            }
            // now, for sake of simplicity , only echo commands back to the client
            _, err = c.Write(buf[:n])
            if err != nil {
                // if i can't write back send an exit command to autoSendRoutine and exit
                ch <- []byte("exit")
                break
            }
        }
    }
    
    // this goroutine reads from the sensors and write to the channel , so data are sent
    // to the client if a client is connected
    func dummyReadDataRoutine(ch chan []byte, wg *sync.WaitGroup) {
        x := 0
        for x < 100 {
            // read data from the sensors every 5 seconds
            time.Sleep(1 * time.Second)
            // read first data and send it
            ch <- []byte("data1\n")
            // read second data and send it
            ch <- []byte("data2\n")
            // read third data and send it
            ch <- []byte("data3\n")
            x++
        }
        wg.Done()
    }
    
    
    func main() {
        // create a BUFFERED CHANNEL
        ch := make(chan []byte, 1)
        wg := sync.WaitGroup{}
        wg.Add(2)
        // launch the goruotines that handle the socket connections
        // and read data from the sensors
        go dummyReadDataRoutine(ch, &wg)
        go launchServerUDS(ch, &wg)
        wg.Wait()
    }
    
    
    主程序包
    进口(
    “净额”
    “操作系统”
    “同步”
    “时间”
    )
    const socketName string=“./test_socket”
    //创建套接字并接受客户端连接
    func launchServerUDS(ch chan[]字节,wg*sync.WaitGroup){
    推迟工作组完成()
    如果错误:=os.RemoveAll(socketName);错误!=nil{
    返回
    }
    l、 err:=net.Listen(“unix”,socketName)
    如果错误!=零{
    返回
    }
    延迟l.关闭()
    为了{
    conn,err:=l.Accept()
    如果错误!=零{
    返回
    }
    //此goroutine在连接客户端时启动
    //侦听和回显命令的例程
    go commandsHandlerRoutine(康涅狄格州,ch)
    //将从传感器读取的数据发送到客户端的例程
    自动结束例行程序(连接,通道)
    }
    }
    //向客户端发送数据的例程
    func自动结束例程(c网络连接,通道[]字节){
    为了{
    
    资料:=JimB给出了一个很好的解释,所以我认为他的答案更好

    我在这个答案中包含了我的部分解决方案

    我认为我的代码是清晰和简化的,但正如Jim所说,我可以做得更简单和更清晰。我将我的旧代码发布,以便人们能够更好地理解如何发布更简单的代码,而不是像我那样做得一团糟

    正如chmike所说,我的问题与我所想的套接字无关,只是与通道有关。在无缓冲通道上写入是问题之一。将无缓冲通道更改为缓冲通道后,问题得到了解决。无论如何,此代码不是“好代码”并且可以按照JimB在回答中所写的原则进行改进

    下面是新代码:

    package main
    
    import (
        "net"
        "os"
        "sync"
        "time"
    )
    
    const socketName string = "./test_socket"
    
    // create the socket and accept clients connections
    func launchServerUDS(ch chan []byte, wg *sync.WaitGroup) {
        defer wg.Done()
        if err := os.RemoveAll(socketName); err != nil {
            return
        }
        l, err := net.Listen("unix", socketName)
        if err != nil {
            return
        }
        defer l.Close()
        for {
            conn, err := l.Accept()
            if err != nil {
                return
            }
            // this goroutine are launched when a client is connected
            // routine that listen and echo commands
            go commandsHandlerRoutine(conn, ch)
            // routine to send data read from the sensors to the client
            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 commands received from the client
    func commandsHandlerRoutine(c net.Conn, ch chan []byte) {
        for {
            buf := make([]byte, 1024)
            n, err := c.Read(buf)
            if err != nil {
                // if i can't read send an exit command to autoSendRoutine and exit
                ch <- []byte("exit")
                break
            }
            // now, for sake of simplicity , only echo commands back to the client
            _, err = c.Write(buf[:n])
            if err != nil {
                // if i can't write back send an exit command to autoSendRoutine and exit
                ch <- []byte("exit")
                break
            }
        }
    }
    
    // this goroutine reads from the sensors and write to the channel , so data are sent
    // to the client if a client is connected
    func dummyReadDataRoutine(ch chan []byte, wg *sync.WaitGroup) {
        x := 0
        for x < 100 {
            // read data from the sensors every 5 seconds
            time.Sleep(1 * time.Second)
            // read first data and send it
            ch <- []byte("data1\n")
            // read second data and send it
            ch <- []byte("data2\n")
            // read third data and send it
            ch <- []byte("data3\n")
            x++
        }
        wg.Done()
    }
    
    
    func main() {
        // create a BUFFERED CHANNEL
        ch := make(chan []byte, 1)
        wg := sync.WaitGroup{}
        wg.Add(2)
        // launch the goruotines that handle the socket connections
        // and read data from the sensors
        go dummyReadDataRoutine(ch, &wg)
        go launchServerUDS(ch, &wg)
        wg.Wait()
    }
    
    
    主程序包
    进口(
    “净额”
    “操作系统”
    “同步”
    “时间”
    )
    const socketName string=“./test_socket”
    //创建套接字并接受客户端连接
    func launchServerUDS(ch chan[]字节,wg*sync.WaitGroup){
    推迟工作组完成()
    如果错误:=os.RemoveAll(socketName);错误!=nil{
    返回
    }
    l、 err:=net.Listen(“unix”,socketName)
    如果错误!=零{
    返回
    }
    延迟l.关闭()
    为了{
    conn,err:=l.Accept()
    如果错误!=零{
    返回
    }
    //此goroutine在连接客户端时启动
    //侦听和回显命令的例程
    go commandsHandlerRoutine(康涅狄格州,ch)
    //将从传感器读取的数据发送到客户端的例程
    自动结束例行程序(连接,通道)
    }
    }
    //向客户端发送数据的例程
    func自动结束例程(c网络连接,通道[]字节){
    为了{
    
    数据:=主要问题在于功能:

    func sendDataToClient(data []byte, ch chan []byte) {
        select {
        case ch <- data:
            // if I put a little sleep here, no problems
            // if I remove the sleep, only data1 is sent to the client
            // time.Sleep(1 * time.Millisecond)
        default:
    }
    
    您还应该小心地在通道上传递切片,因为很容易忘记哪个逻辑进程拥有所有权,并将修改备份阵列