Я пытаюсь настроить способ связи между центральным сервером, на котором установлен Go, и парком устройств IoT (также работающим на Go).
Для каждого устройства оно подключается к центральному серверу через постоянный TCPConn. Эти устройства находятся за маршрутизатором (ами). Центральный сервер сохраняет это соединение и отправляет / получает сообщения через него. Прямо сейчас, это полностью функционально и работает.
Однако теперь передача сообщений становится достаточно сложной, так что становится необходимой утилита, предоставляемая HTTP, а не просто TCP.
Я пыталсянапишите версию http.Transport, которая возвращает указанное соединение. Однако я не могу предоставить и вернуть действительное соединение с помощью функций Dial / DialContext.
IoT-устройство
func main() {
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
tcpAddr, err := net.ResolveTCPAddr("tcp", "###.###.###.###:8533")
if err != nil {
panic(err)
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
panic(err)
}
err = conn.SetKeepAlive(true)
if err != nil {
panic(err)
}
err = conn.SetKeepAlivePeriod(time.Second * 10)
if err != nil {
panic(err)
}
fmt.Println("Listening")
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
Центральный сервер
func main() {
tcpAddr, err := net.ResolveTCPAddr("tcp", port)
if err != nil {
panic(err)
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
panic(err)
}
conn, err := listener.AcceptTCP()
if err != nil {
panic(err)
}
fmt.Println("Received conn, attempting to send HTTP through connection")
dialFunc := func(network, addr string) (net.Conn, error) {
return conn, nil
}
t := http.Transport{
Dial: dialFunc,
}
client := http.Client{
Transport: &t,
}
fmt.Println("Making request")
res, err := client.Get("http://www.shouldNotMatter.com:8080/foo") // HANGS HERE
if err != nil {
fmt.Println(err)
}
fmt.Println("Received response")
defer res.Body.Close()
if res.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
bodyString := string(bodyBytes)
fmt.Println(bodyString)
} else {
fmt.Println(res)
}
}
При использованииотладчик, чтобы увидеть, где он висит, кажется, что он застревает в операторе select во время обхода pconn. Строка 2420 в https://golang.org/src/net/http/transport.go?s=3397:10477