Go Unix Domain Socket:绑定地址已在使用中

Posted

技术标签:

【中文标题】Go Unix Domain Socket:绑定地址已在使用中【英文标题】:Go Unix Domain Socket: bind address already in use 【发布时间】:2017-05-14 10:24:34 【问题描述】:

我有以下服务器代码,它通过 unix 域套接字进行侦听

package main

import (
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"
)

func echoServer(c net.Conn) 
    for 
        buf := make([]byte, 512)
        nr, err := c.Read(buf)
        if err != nil 
            return
        

        data := buf[0:nr]
        println("Server got:", string(data))
        _, err = c.Write(data)
        if err != nil 
            log.Fatal("Writing client error: ", err)
        
    


func main() 
    log.Println("Starting echo server")
    ln, err := net.Listen("unix", "/tmp/go.sock")
    if err != nil 
        log.Fatal("Listen error: ", err)
    

    sigc := make(chan os.Signal, 1)
    signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
    go func(ln net.Listener, c chan os.Signal) 
        sig := <-c
        log.Printf("Caught signal %s: shutting down.", sig)
        ln.Close()
        os.Exit(0)
    (ln, sigc)

    for 
        fd, err := ln.Accept()
        if err != nil 
            log.Fatal("Accept error: ", err)
        

        go echoServer(fd)
    

当我使用 Ctrl+C 关闭它时,会捕获信号并关闭套接字。当我重新运行程序时,一切正常。

但是,如果正在运行的进程被突然杀死,并且如果程序重新启动,则侦听失败并出现错误Listen error: listen unix /tmp/go.sock: bind: address already in use

如何优雅地处理这个?

我问这个的原因是:我知道突然杀死不是正常的方法,但是我的程序会作为守护进程自动启动,如果我的守护进程重新启动,我希望能够再次监听套接字没有这个错误。

这也可能是因为先前的实例正在运行,我理解。这里的问题是如何在 Go 中以编程方式识别并处理这种情况。正如答案here 中所指出的,可以在C 程序中使用SO_REUSEADDR。 Go中有这样的可能性吗?另外,C 程序如何处理这种多实例问题。

【问题讨论】:

服务器无法连接。客户端连接。服务器侦听并接受连接。如果绑定时得到 'address already in use,则该地址已在使用中,可能是由您的程序的先前实例使用的,可能是因为“突然杀死”不是终止它的方法。 @EJP Wow.. -1 是一个错字,我写的是connect 而不是listen。你说突然杀戮不是终止它的方法,这很有趣。我知道。但是,当使用我的程序的客户以某种方式杀死它时,我想考虑如何优雅地处理它!在 C 中,我可以使用重用套接字,但是如何在 Go 中的问题中做到这一点。在给问题打负分之前三思! SO_REUSEADDR 与此处无关(请参阅***.com/questions/15716302/so-reuseaddr-and-af-unix)。如果您不需要该文件并想再次绑定到该地址,请删除该文件。 见related question。总之,还要处理signal.Notify中的os.Killsyscall.SIGTERM信号。 这个答案解释了如何检测一个工作实例是否正在监听,它的问题有更多关于为什么要删除 unix 域套接字的详细信息:***.com/a/13719866/7496656 【参考方案1】:

您需要捕捉信号并进行清理;一些示例代码:

func HandleSIGINTKILL() chan os.Signal 
    sig := make(chan os.Signal, 1)

    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

    return sig


...
go func() 
    <-HandleSIGINTKILL()

    log.Info("Received termination signal")
    // Cleanup code here

    os.Exit(0)
()

如果你kill -9进程,这当然行不通;您将需要手动移除套接字(或让您的 init 系统为您完成)。

【讨论】:

以上是关于Go Unix Domain Socket:绑定地址已在使用中的主要内容,如果未能解决你的问题,请参考以下文章

【socket】关于Unix域套接字(Unix Domain Socket)

Unix domain socket 简介

Unix域套接字-Unix Domain Socket(转)

Unix domain socket

unix domain socket 的 UDP 通信

Unix domain sockets