SMPP - Как нужно закрыть соединение по таймауту, если клиент не отвечает в течение 10 секунд?

Использую библиотеку для работы с SMPP (https://github.com/fiorix/go-smpp) - не много дописал свой код в следующий обработчик:

// -----------------------------------------------
// handleConnection new clients.
// -----------------------------------------------
func (_srv *ServerSmpp) handleConnection(_cfg *ConfigSmpp, c *conn) {
    defer c.Close()

    if err := _srv.auth(_cfg, c); err != nil {
        if err != io.EOF {
            log.Printf("smpp_server: server auth failed: %s\n", err)
        }
        return
    }

    notify := make(chan error)

    go func() {
        for {
            pb, err := c.Read()
            if err != nil {
                notify <- err
                break
            }

            err = _srv.Handler(_srv.RemoteProvider, c, pb)
            if err != nil {
                fmt.Printf("%s\n", err)
                notify <- err
                break
            }
        }
    }()


    for {
        select {
        case err:= <-notify:
            if io.EOF == err {
                fmt.Printf("Smpp server (read): %s\n", err)
                c.Close()
                return
            }
      case <-time.After(time.Second * 10):
            fmt.Printf("Client disconnected by timeout.\n")
            c.Close()
            return
        }
    }
}

Когда этот код работает, то не Важно клиент передает данные или нет - он сбрасывает подключение каждые 10 секунду, а как сделать что бы он сбрасывал подключение только когда клиент не активный ?


Ответы (1 шт):

Автор решения: Инди Путный

Для решения этой задачи я использовал метод(нашел на просторах сети), данная функция проверяет если клиент не доступный то возвращает ошибку:

// -----------------------------------------------
// Check client connect or disconnected
// -----------------------------------------------
func (c *conn) CheckIsActive() error {
    var sysErr error = nil
    rc, err := c.rwc.(syscall.Conn).SyscallConn()
    if err != nil {
        return err
    }
    err = rc.Read(func(fd uintptr) bool {
        var buf = []byte{0}
        n, _, err := syscall.Recvfrom(int(fd), buf, syscall.MSG_PEEK|syscall.MSG_DONTWAIT)
        switch {
        case n == 0 && err == nil:
            sysErr = io.EOF
        case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
            sysErr = nil
        default:
            sysErr = err
        }
        return true
    })
    if err != nil {
        return err
    }

    return sysErr
}

Дальше я создал корутину, которая вызывает метод AutoRemoveDisconnectedClients() для регулярной проверки подключённых клиентов и если клиент больше двух раз возвращает ошибку то я его отключаю, если за указанное время ошибок не было то счетчик сбрасывается:

//5 - это время в секундах для вызова проверки
go srv.AutoRemoveDisconnectedClients(5)

    // -----------------------------------------------
    // Check client connect or disconnected (if disconnected then remove from map)
    // _srv.listConnectClients -> map[string]Conn - ключ IP:PORT
    // -----------------------------------------------
    func (_srv *ServerSmpp) AutoRemoveDisconnectedClients(d time.Duration) {
        for {
            time.Sleep(d * time.Second)
            for key, client := range _srv.listConnectClients {
    
                err := client.CheckIsActive()
                log.Printf("-----------------------------------------------")
    
                if err != nil {
    
                    if client.GetNumberOfAttemptsIO() > 2 {
                        if err != nil {
                            log.Printf("Detected client %s closed LAN connection: %s", key, err)
                        } else {
                            log.Printf("Detected client %s closed LAN connection.", key)
                        }
                        err := client.Close()
                        if err != nil {
                            log.Printf("Error close client %s : %s", key, err)
                        }
                        delete(_srv.listConnectClients, key)
                        log.Printf("Client %s - removed from smpp server", key)
                    } else {
                        client.AddNumberOfAttemptsIO()
                    }
                } else {
                    client.ResetNumberOfAttemptsIO()
                }
    
            }
    
        }
    }
→ Ссылка