2012-11-16 3 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

Dunno, 나는 그것을 시험해보아야 할 것이다. – 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

흠, 멋진 생각. 나는 bool이 채널을 사용하는 것뿐만 아니라 잘 할 것이라고 생각한다. '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

관련 문제