2012-11-16 13 views
17

私はGoで正常にリスニングサーバを停止する方法を見つけようとしています。 listen.Acceptブロックのため、リスニングソケットを閉じて終了を知らせる必要がありますが、関連するエラーがエクスポートされていないため、他のエラーとは区別できません。Goでリスニングサーバを停止するには

これよりも優れていますか? 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() 
} 

に以下のコードでFIXMEを参照してください。この

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 
私は Accept failedメッセージを非表示にする

を出力しますが、明らかに私は他のエラーをマスクしたくないAccept報告することができます。私はもちろん、use of closed network connectionのエラーテストを見ることができますが、それは本当に醜いでしょう。私は、もし私が仮定したならば、私が閉じようとしているエラーを無視しようとしているという旗を立てることができました。

答えて

4

は一部、右accept()コールの後、あなたのループ内のフラグを、あなたのmainからそれを反転し、サーバソケット「非スタック」を得るためにあなたのリスニングポートにを接続する「停止する時間である」を確認してください。これは古い"self-pipe trick"と非常によく似ています。これらの行の間で

+0

!実際の接続が入っている競合状態がありますが、回避する必要があります。 –

+0

はい、本当です。あなたはその逆を試すことができます - 常に最初からその単一の内部接続を持って、それをコントロールチャンネルとして使用してください。 –

1

何かが、この場合にはうまくいくかもしれない、私は願っています:

// 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

いいアイデアありがとう!私は 'Syscall.Accept()'がエラーを返す(私が見た最近の例では、ソケットがなくなったという)公式の停止を区別することはできませんか? –

+0

ダニー、私はそれを試してみる必要があります。 – zzzz

10

が、私はそれが接続を閉じる前に信号を送信するためにes.doneを使用してこれを処理します。次のコードに加えて、make(chan bool、1)を使用してes.doneを作成して、ブロックせずに単一の値を入れる必要があります。

// 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

うーん、良いアイデア。私は 'ブール'だけでなく、チャネルを使用して同様に私が思い付いた解決策はほとんど何をすると思います。 'stop'と' serve'を同期させるにはチャンネルは必要ですが、いつ停止したのかを知ることができます。 –

+4

チャネルをバッファリングしたり、チャネルにメッセージを送信したりしないでください。ただそれを閉じる。 – Dustin

-3

これは、ローカル開発には十分な簡単な方法です。きちんとした考えである

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


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

プロセス全体を終了することは、サーバーを閉じる有効な方法ではありません! – Setomidor

関連する問題