英文:
Freeing a port before running a server instant
问题
我想在基于Linux的系统上运行一个go
服务器。在某些情况下,我发现相同的端口被另一个应用程序占用,所以我想要终止该端口上正在运行的进程,并运行我的服务器。因此,我编写了以下代码:
func main() {
host := "127.0.0.1"
port := "8070"
server := http.Server{
Addr: "127.0.0.1:8070",
}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "无法监听端口 %q:%s \n", port, err)
// 终止该端口上正在运行的进程
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
fmt.Printf("无法终止端口 %q 上的进程\n", port)
} else {
fmt.Printf("TCP端口 %q 可用\n", port)
server.ListenAndServe()
}
} else {
ln.Close()
server.ListenAndServe()
}
}
我能够得到响应TCP端口8070可用
,这意味着有另一个正在运行的进程,并且已经被终止,但是我的应用程序直接关闭,而没有在已经关闭的端口上运行我的服务器!
hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
无法监听端口 "8070":listen tcp :8070: bind: address already in use
TCP端口 "8070" 可用
hajsf@AIS-DM-YOUSEF-L:~/myapp$
在原始终端中(旧的应用程序实例),我得到了以下输出:
hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
signal: killed
hajsf@AIS-DM-YOUSEF-L:~/myapp$
英文:
I want to run a go
server at linux
based system, it happened in soe cases that i found the same port is busy with another app, so i want to kill the running process at that port, and run my server instead, so I wrote the below code:
func main() {
host := "127.0.0.1"
port := "8070"
server := http.Server{
Addr: "127.0.0.1:8070",
}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
// kill the running process at this port
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
fmt.Printf("Failed to kill process at Port %q\n", port)
} else {
fmt.Printf("TCP Port %q is available\n", port)
server.ListenAndServe()
}
} else {
ln.Close()
server.ListenAndServe()
}
}
I was able to get the response TCP Port 8070 is available
whihc means there was another running process and it had been killed, but my app is closed directly without running my server at the same port which had been already closed!
hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
Can't listen on port "8070": listen tcp :8070: bind: address already in use
TCP Port "8070" is available
hajsf@AIS-DM-YOUSEF-L:~/myapp$
In the origional terminal (the old instance of the app0 I got;
hajsf@AIS-DM-YOUSEF-L:~/myapp$ go run myapp
signal: killed
hajsf@AIS-DM-YOUSEF-L:~/myapp$
答案1
得分: 1
在CentOS 7上运行良好,但在Ubuntu服务器上出现相同的问题。@MoiioM的答案是正确的。但是,如果另一个应用程序不是golang应用程序本身,这里有另一种方法:在套接字上设置SO_REUSEADDR标志。
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"syscall"
"golang.org/x/sys/unix"
)
func reusePort(network, address string, conn syscall.RawConn) error {
return conn.Control(func(descriptor uintptr) {
syscall.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
})
}
func main() {
port := "8070"
server := http.Server{}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "无法监听端口 %q:%s \n", port, err)
// 杀死在该端口上运行的进程
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
fmt.Printf("无法杀死端口 %q 上的进程\n", port)
} else {
fmt.Printf("TCP端口 %q 可用\n", port)
config := &net.ListenConfig{Control: reusePort}
listener, err := config.Listen(context.Background(), "tcp", ":"+port)
if err != nil {
panic(err)
}
if err := server.Serve(listener); err != nil {
panic(err)
}
}
} else {
if err := server.Serve(ln); err != nil {
panic(err)
}
}
}
英文:
worked well on CentOS 7, but same issue with a Ubuntu server. @MoiioM 's answer is fine. But if another app is not the golang app itself, here is another way: set a SO_REUSEADDR flag on the socket
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"syscall"
"golang.org/x/sys/unix"
)
func reusePort(network, address string, conn syscall.RawConn) error {
return conn.Control(func(descriptor uintptr) {
syscall.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
})
}
func main() {
port := "8070"
server := http.Server{}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
// kill the running process at this port
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
fmt.Printf("Failed to kill process at Port %q\n", port)
} else {
fmt.Printf("TCP Port %q is available\n", port)
config := &net.ListenConfig{Control: reusePort}
listener, err := config.Listen(context.Background(), "tcp", ":"+port)
if err != nil {
panic(err)
}
if err := server.Serve(listener); err != nil {
panic(err)
}
}
} else {
if err := server.Serve(ln); err != nil {
panic(err)
}
}
}
答案2
得分: 1
您的命令 _, err := exec.Command("fuser", "-k", "8070/tcp").Output()
杀死了进程,但没有清理资源,即端口监听。在父进程被杀死后,端口会进入 TIME_WAIT 状态。
您需要等待一段时间,让您的操作系统/内核清理端口/套接字。
一个更好的选择是处理来自 fuser
的 kill sigint 信号,并进行优雅的关闭。
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"time"
)
func runServer(host, port string) {
server := http.Server{
Addr: host + ":" + port,
}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
go func() {
if err := server.ListenAndServe(); err != nil {
}
}()
// 设置信号捕获
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
// 等待 SIGINT (kill -2)
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
// 处理错误
}
}
func main() {
host := "127.0.0.1"
port := "8070"
ln, err := net.Listen("tcp", host+":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "无法监听端口 %q:%s \n", port, err)
// 杀死在该端口上运行的进程
cmd := exec.Command("fuser", "-k", "-2", "8070/tcp")
fmt.Println("等待")
err = cmd.Run()
if err != nil {
fmt.Printf("无法杀死端口 %q 上的进程\n", port)
} else {
fmt.Printf("TCP 端口 %q 可用\n", port)
runServer(host, port)
}
} else {
ln.Close()
runServer(host, port)
}
}
英文:
As you can see in the response https://stackoverflow.com/questions/11583562/how-to-kill-a-process-running-on-particular-port-in-linux#comment52643643_11596144
Your command _, err := exec.Command("fuser", "-k", "8070/tcp").Output()
kill the process but doesn't cleanup the resource ie: port listening.
The port is put into TIME_WAIT state after the parent process is killed.
And you need to wait some time your OS/Kernel cleanup the port/socket
A better alternative is to handle the kill sigint from fuser
and do a graceful shutdown
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"time"
)
func runServer(host, port string) {
server := http.Server{
Addr: host + ":" + port,
}
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
go func() {
if err := server.ListenAndServe(); err != nil {
}
}()
// Setting up signal capturing
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
// Waiting for SIGINT (kill -2)
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
// handle err
}
}
func main() {
host := "127.0.0.1"
port := "8070"
ln, err := net.Listen("tcp", host+":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
// kill the running process at this port
cmd := exec.Command("fuser", "-k", "-2", "8070/tcp")
fmt.Println("wait")
err = cmd.Run()
if err != nil {
fmt.Printf("Failed to kill process at Port %q\n", port)
} else {
fmt.Printf("TCP Port %q is available\n", port)
runServer(host, port)
}
} else {
ln.Close()
runServer(host, port)
}
}
答案3
得分: 0
我使用以下代码中的panic ... recover
解决了这个问题:
package main
import (
"fmt"
"net"
"net/http"
"onsen/resources"
"onsen/routes"
"os"
"os/exec"
)
func main() {
http.Handle("/webUI/", http.StripPrefix("/webUI/", http.FileServer(http.FS(resources.WebUI))))
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
for key, value := range routes.Urls() {
http.HandleFunc(key, value)
}
runServer()
}
func runServer() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
runServer()
}
}()
host := "127.0.0.1"
port := "8070"
server := http.Server{
Addr: fmt.Sprintf("%v:%v", host, port),
}
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
// kill the running process at this port
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
panic(fmt.Sprintf("Failed to kill process at Port %q\n", port))
} else {
fmt.Printf("TCP Port %q is available\n", port)
fmt.Println("server started...")
if err := server.Serve(ln); err != nil {
panic(err)
}
}
} else {
fmt.Println("server started...")
if err := server.Serve(ln); err != nil {
panic(err)
}
}
}
并且得到了以下输出:
英文:
I was able to solve it with panic ... recover
as below:
package main
import (
"fmt"
"net"
"net/http"
"onsen/resources"
"onsen/routes"
"os"
"os/exec"
)
func main() {
http.Handle("/webUI/", http.StripPrefix("/webUI/", http.FileServer(http.FS(resources.WebUI))))
http.Handle("/www/", http.StripPrefix("/www/", http.FileServer(http.Dir("./www"))))
for key, value := range routes.Urls() {
http.HandleFunc(key, value)
}
runServer()
}
func runServer() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
runServer()
}
}()
host := "127.0.0.1"
port := "8070"
server := http.Server{
Addr: fmt.Sprintf("%v:%v", host, port),
}
ln, err := net.Listen("tcp", ":"+port)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't listen on port %q: %s \n", port, err)
// kill the running process at this port
_, err := exec.Command("fuser", "-k", "8070/tcp").Output()
if err != nil {
panic(fmt.Sprintf("Failed to kill process at Port %q\n", port))
} else {
fmt.Printf("TCP Port %q is available\n", port)
fmt.Println("server started...")
if err := server.Serve(ln); err != nil {
panic(err)
}
}
} else {
fmt.Println("server started...")
if err := server.Serve(ln); err != nil {
panic(err)
}
}
}
And got the output as below:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论