2012-11-16 26 views
17

'da nasıl durdurabilirim Gitme bölümünde bir dinleme sunucusunu durdurmanın bir yolunu bulmaya çalışıyorum. listen.Accept engellendiğinden, sonlandırmak için dinleme soketini kapatmak gerekir, ancak bu hatayı, ilgili hata verilmediğinden diğer hatalardan ayrı olarak anlatamam.Bir Dinleme sunucusunu Go

Bundan daha iyisini yapabilir miyim? serve()

package main 

import (
    "io" 
    "log" 
    "net" 
    "time" 
) 

// Echo server struct 
type EchoServer struct { 
    listen net.Listener 
    done chan bool 
} 

// Respond to incoming connection 
// 
// Write the address connected to then echo 
func (es *EchoServer) respond(remote *net.TCPConn) { 
    defer remote.Close() 
    _, err := io.Copy(remote, remote) 
    if err != nil { 
     log.Printf("Error: %s", err) 
    } 
} 

// Listen for incoming connections 
func (es *EchoServer) serve() { 
    for { 
     conn, err := es.listen.Accept() 
     // FIXME I'd like to detect "use of closed network connection" here 
     // FIXME but it isn't exported from net 
     if err != nil { 
      log.Printf("Accept failed: %v", err) 
      break 
     } 
     go es.respond(conn.(*net.TCPConn)) 
    } 
    es.done <- true 
} 

// Stop the server by closing the listening listen 
func (es *EchoServer) stop() { 
    es.listen.Close() 
    <-es.done 
} 

// Make a new echo server 
func NewEchoServer(address string) *EchoServer { 
    listen, err := net.Listen("tcp", address) 
    if err != nil { 
     log.Fatalf("Failed to open listening socket: %s", err) 
    } 
    es := &EchoServer{ 
     listen: listen, 
     done: make(chan bool), 
    } 
    go es.serve() 
    return es 
} 

// Main 
func main() { 
    log.Println("Starting echo server") 
    es := NewEchoServer("127.0.0.1:18081") 
    // Run the server for 1 second 
    time.Sleep(1 * time.Second) 
    // Close the server 
    log.Println("Stopping echo server") 
    es.stop() 
} 

aşağıda kodunda FIXME gör Bu

2012/11/16 12:53:35 Starting echo server 
2012/11/16 12:53:36 Stopping echo server 
2012/11/16 12:53:36 Accept failed: accept tcp 127.0.0.1:18081: use of closed network connection 
Ben Accept failed mesajı gizlemek istiyorum

basar, ama açıkçası diğer hataları maskelemek istemiyoruz Accept bildirebilirsiniz. Tabii ki use of closed network connection için hata testine bakabilirim ama bu gerçekten çirkin olurdu. Bir bayrak ayarlayabilirim ki, sanırım ayarlanmışsa, hataları kapatmak ve göz ardı etmek üzereyim - Daha iyi bir yol var mı?

cevap

4

kontrol bazı sonra, sağ accept() çağrısından sonra sizin döngüde bayrak senin main onu çevirmek, daha sonra "un-sıkışmış" sunucu soketi almak için dinleme bağlantı noktasına bağlamak "durdurmak için vakit geldi". Bu eski "self-pipe trick"'a çok benzer. Bu satırların arasında

+0

! Çalışmak zorunda olduğunuz gerçek bağlantıları olan bir yarış durumu var. –

+0

Evet, doğru. Bunun tersini deneyebilirsiniz - her zaman başlangıçtan bu tek iç bağlantıya sahip ol ve bunu kontrol kanalı olarak kullan. –

1

şey bu durumda işe yarayabilecek, umarım:

// Listen for incoming connections 
func (es *EchoServer) serve() { 
     for { 
       conn, err := es.listen.Accept() 
       if err != nil { 
        if x, ok := err.(*net.OpError); ok && x.Op == "accept" { // We're done 
          log.Print("Stoping") 
          break 
        } 

        log.Printf("Accept failed: %v", err) 
        continue 
       } 
       go es.respond(conn.(*net.TCPConn)) 
     } 
     es.done <- true 
} 
+0

İyi bir fikir teşekkürler! Syscall.Accept() 'dan bir resmi bir duraktan ayrı bir şekilde anlatacağından emin değilim (bir hata döndürdüğüm son bir örnek) –

+0

Dunno, bununla denemeliyim. – zzzz

10

Ben bağlantı kapanmadan önce bir sinyal göndermek için es.done kullanarak bu yönetecek. Aşağıdaki koda ek olarak, make (chan bool, 1) ile es.done yaratmanız gerekir, böylece bloklamadan tek bir değer koyabiliriz.

// Listen for incoming connections 
func (es *EchoServer) serve() { 
    for { 
    conn, err := es.listen.Accept() 
    if err != nil { 
     select { 
     case <-es.done: 
     // If we called stop() then there will be a value in es.done, so 
     // we'll get here and we can exit without showing the error. 
     default: 
     log.Printf("Accept failed: %v", err) 
     } 
     return 
    } 
    go es.respond(conn.(*net.TCPConn)) 
    } 
} 

// Stop the server by closing the listening listen 
func (es *EchoServer) stop() { 
    es.done <- true // We can advance past this because we gave it buffer of 1 
    es.listen.Close() // Now it the Accept will have an error above 
} 
+0

Hmm, güzel fikir. Bence bir 'bool', kanalın yanı sıra geldiğim çözümün de olduğu gibi kullandığını düşünüyorum. Yine de, 'stop' işlevini "serve" ile senkronize etmek için yine de kanallara ihtiyacınız var, böylece ne zaman durduğunu biliyorsunuz. –

+4

Kanalın tamponlanmasını veya kanaldan bir mesaj göndermeyi zahmet etmeyin. Sadece kapat. – Dustin

-3

Yerel geliştirme için yeterince iyi olan basit bir yol.

http://www.sergiotapia.me/how-to-stop-your-go-http-server/


düzgün bir fikirdir
package main 

import ( 
    "net/http" 
    "os" 

    "github.com/bmizerany/pat" 
) 

var mux = pat.New() 

func main() { 
    mux.Get("/kill", http.HandlerFunc(kill)) 
    http.Handle("/", mux) 
    http.ListenAndServe(":8080", nil) 
} 

func kill(w http.ResponseWriter, r *http.Request) { 
    os.Exit(0) 
} 
+1

Tüm işlemden çıkmak bir sunucuyu kapatmak için geçerli bir yol değildir! – Setomidor