2012-11-16 37 views
17

Tôi đã cố gắng tìm cách dừng máy chủ nghe trong Go một cách duyên dáng. Vì khối listen.Accept cần phải đóng ổ cắm nghe để báo hiệu kết thúc, nhưng tôi không thể nói lỗi đó ngoài bất kỳ lỗi nào khác vì lỗi liên quan không được xuất.Làm cách nào để dừng máy chủ Nghe trong Go

Tôi có thể làm tốt hơn điều này không? Xem FIXME trong các mã dưới đây trong 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() 
} 

này in

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 

Tôi muốn giấu thông điệp Accept failed, nhưng rõ ràng là tôi không muốn che giấu lỗi khác Accept có thể báo cáo. Tôi có thể tất nhiên nhìn vào kiểm tra lỗi cho use of closed network connection nhưng điều đó sẽ thực sự xấu xí. Tôi có thể thiết lập một lá cờ nói rằng tôi sắp đóng và bỏ qua lỗi nếu điều đó được đặt Tôi giả sử - Có cách nào tốt hơn không?

Trả lời

4

Kiểm tra một số "là nó thời gian để dừng lại" cờ trong vòng lặp của bạn ngay sau khi accept() cuộc gọi, sau đó lật nó từ main của bạn, sau đó kết nối đến cổng nghe của bạn để có được ổ cắm máy chủ "un-kẹt". Điều này rất giống với số "self-pipe trick" cũ.

+0

Đó là một ý tưởng gọn gàng! Có một điều kiện chủng tộc với các kết nối thực sự đến mặc dù bạn cần phải làm việc xung quanh. –

+0

Vâng, đúng vậy. Bạn có thể thử một nghịch đảo của điều đó - luôn luôn có kết nối nội bộ duy nhất từ ​​đầu và sử dụng nó như một kênh điều khiển. –

1

Something trong những dòng này có thể làm việc trong trường hợp này, tôi hy vọng:

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

Cảm ơn bạn! Tôi không chắc chắn nó sẽ cho biết một điểm dừng chính thức từ 'Syscall.Accept()' trả về một lỗi (một ví dụ gần đây tôi đã nhìn thấy là quá trình chạy ra khỏi ổ cắm) phải không? –

+0

Dunno, tôi sẽ phải thử/thử nghiệm với nó. – zzzz

10

tôi sẽ xử lý việc này bằng cách sử dụng es.done để gửi một tín hiệu trước khi nó đóng kết nối. Ngoài mã sau bạn cần tạo es.done với make (chan bool, 1) để chúng ta có thể đặt một giá trị duy nhất vào nó mà không bị chặn.

// 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, ý tưởng hay. Tôi nghĩ rằng một bool' sẽ làm cũng như sử dụng các kênh mặc dù đó là khá nhiều các giải pháp tôi đã đưa ra. Bạn vẫn cần kênh mặc dù đồng bộ hóa 'stop' với' serve' để bạn biết khi nào nó dừng lại. –

+4

Đừng làm phiền kênh hoặc gửi tin nhắn xuống kênh. Chỉ cần đóng nó lại. – Dustin

-3

Đây là một cách đơn giản đủ tốt để phát triển địa phương.

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

Thoát khỏi toàn bộ quá trình không phải là cách hợp lệ để đóng máy chủ! – Setomidor

Các vấn đề liên quan