2012-11-16 30 views
17

में एक सुनवाई सर्वर को कैसे रोकूं मैं गो में एक सुनवाई सर्वर को रोकने के लिए एक रास्ता खोजने का प्रयास कर रहा हूं। क्योंकि listen.Accept ब्लॉक अंत को सिग्नल करने के लिए सुनना सॉकेट बंद करना आवश्यक है, लेकिन मैं उस त्रुटि को किसी भी अन्य त्रुटियों से अलग नहीं बता सकता क्योंकि प्रासंगिक त्रुटि निर्यात नहीं की जाती है।मैं

क्या मैं इससे बेहतर कर सकता हूं? नीचे दिए गए कोड में FIXME देखें 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() 
} 

में यह

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 का उपयोग करके इस संभाल होगा। निम्नलिखित कोड के अतिरिक्त आपको बनाने के साथ es.done बनाने की आवश्यकता होगी (चैन बूल, 1) ताकि हम बिना किसी अवरोध के एक मूल्य डाल सकें।

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

हम्म, अच्छा विचार। मुझे लगता है कि एक 'बूल' चैनल के साथ-साथ चैनल का उपयोग करेगा, हालांकि मैं जिस समाधान के साथ आया था, उतना ही काफी है। 'स्टॉप' के साथ 'स्टॉप' के साथ सिंक्रनाइज़ करने के लिए आपको अभी भी चैनल की आवश्यकता है ताकि आप जान सकें कि यह कब रुक गया है। –

+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