英文:
How do I stop a Listening server in Go
问题
我一直在尝试找到一种优雅地停止Go中的监听服务器的方法。因为listen.Accept
会阻塞,所以关闭监听套接字以发出结束信号是必要的,但是我无法将相关错误与其他错误区分开,因为相关错误没有被导出。
我能做得比这更好吗?请参见下面代码中的serve()
中的FIXME
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
,但那将非常丑陋。我可以设置一个标志,表示我即将关闭并忽略错误,如果设置了该标志的话 - 是否有更好的方法?
英文:
I've been trying to find a way to stop a listening server in Go gracefully. Because listen.Accept
blocks it is necessary to close the listening socket to signal the end, but I can't tell that error apart from any other errors as the relevant error isn't exported.
Can I do better than this? See FIXME
in the code below in 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()
}
This prints
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
I'd like to hide the Accept failed
message, but obviously I don't want to mask other errors Accept
can report. I could of course look in the error test for use of closed network connection
but that would be really ugly. I could set a flag saying I'm about to close and ignore errors if that was set I suppose - Is there a better way?
答案1
得分: 16
我会使用es.done来发送一个信号,在关闭连接之前处理它。除了下面的代码,你需要使用make(chan bool, 1)来创建es.done,这样我们就可以在不阻塞的情况下放入一个单一的值。
// 监听传入的连接
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
select {
case <-es.done:
// 如果我们调用了stop(),那么es.done中将会有一个值,所以我们会到达这里,可以退出而不显示错误。
default:
log.Printf("Accept failed: %v", err)
}
return
}
go es.respond(conn.(*net.TCPConn))
}
}
// 通过关闭监听来停止服务器
func (es *EchoServer) stop() {
es.done <- true // 我们可以继续执行,因为我们给它一个缓冲区为1
es.listen.Close() // 现在Accept将会有一个错误
}
英文:
I would handle this by using es.done to send a signal before it closes the connection. In addition to the following code you'd need to create es.done with make(chan bool, 1) so that we can put a single value in it without blocking.
// 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
}
答案2
得分: 8
在accept()
调用之后,在你的循环中检查一些“是否是停止的时间”标志,然后从你的main
中翻转它,然后连接到你的监听端口以解除服务器套接字的“卡住”状态。这与旧的“自管道技巧”非常相似。
英文:
Check some "is it time to stop" flag in your loop right after the accept()
call, then flip it from your main
, then connect to your listening port to get server socket "un-stuck". This is very similar to the old "self-pipe trick".
答案3
得分: 2
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
if x, ok := err.(*net.OpError); ok && x.Op == "accept" { // 我们完成了
log.Print("停止")
break
}
log.Printf("接受失败:%v", err)
continue
}
go es.respond(conn.(*net.TCPConn))
}
es.done <- true
}
英文:
Something among these lines might work in this case, I hope:
// 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
}
答案4
得分: -7
这是一个足够简单的方法,适用于本地开发。
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)
}
英文:
Here's a simple way that's good enough for local development.
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论