如何在 Go 中將 TCP 連線傳遞給子程序

2021-10-04 21:55:06 字數 1862 閱讀 3526

熟悉 linux 的開發者都知道,重啟系統 sshd 服務時,當前連線是不會丟失的。

比如,在需要修改 sshd 服務埠號的時候,只要修改/etc/ssh/sshd_config,然後執行systemctl restart sshd即可,當前會話不會有任何中斷。

這是因為,openssh使用了程序模型,主程序只負責埠監聽。新連線建立的時候,主程序會 fork 出子程序,並將 tcp 連線傳遞給子程序處理。主程序停止不影響子程序繼續執行,因此重啟服務不影響已經建立的會話。

我嘗試在 go 中實現相同的特性,並取得了成功。

我在此簡要介紹一下,在 go 中如何將 tcp 連線傳遞給子程序。

相關**在此:antssh/antsshd

go 中的syscall.forkexec是真正意義上的 fork,但是使用起來比較麻煩,因此我選擇使用os/exec

為此,我增加了乙個-worker引數。主程序會通過os.executable()方法來獲取自身可執行檔案的完整路徑,在建立子程序的時候,會執行相同的命令,並追加該引數。這樣,新建立出來的程序就知道自己是子程序了。

主程序監聽埠,每次接受新連線的時候,就會建立子程序,並將 tcp 連線傳遞給子程序。

具體方法是,使用net.tcpconn.file()為乙個 tcp 連線建立 fd 拷貝,並通過exec.command.extrafiles引數傳遞給子程序。

按照官方文件的說法,net.tcpconn.file()建立 fd 拷貝後,關閉原本的 tcp 連線和關閉此 fd 不會互相影響。

進一步,當子程序攜帶此 fd 啟動後,主程序可以正常關閉該 fd。

// 主程序

// 獲取 tcp 連線的 fd 拷貝

var f *os.file

if f, err = c.(*net.tcpconn).file(); err != nil

defer f.close() // fd 傳遞給子程序之後,也可以安全關閉

// 建立子程序

cmd.dir, _ = os.getwd()

cmd.env = os.environ()

cmd.stdout = os.stdout

cmd.stderr = os.stderr

cmd.extrafiles = *os.file // 此處將 fd 傳遞給了子程序

if err = cmd.start(); err != nil

linux 程序中,fd0 = stdin, fd 1 = stdout, fd 2 = stderr,開啟的其他檔案會順延下去。

因此,通過上述方法建立出來的子程序,可以直接使用fd 3來獲取該 tcp 連線,並通過net.fileconn()復原出net.conn,進行下一步操作。

// 子程序

// 從 fd 3 獲取連線

var c net.conn

if c, err = net.fileconn(os.newfile(3, "connection")); err != nil

一般情況下,會通過sigterm或者sigint來中斷主程序,為了保證子程序不會同時中斷,需要在子程序中使用signal.notify捕獲該訊號並丟棄。

如何在socket程式設計的Tcp連線中實現心跳協議

跳包之所以叫心跳包是因為 它像心跳一樣每隔固定時間發一次,以此來告訴伺服器,這個客戶端還活著。事實上這是為了保持長連線,至於這個包的內容,是沒有什麼特別規定的,不過一般都是很小的包,或者只包含包頭的乙個空包。在tcp的機制裡面,本身是存在有心跳包的機制的,也就是tcp的選項 so keepalive...

如何在C 中將filetime時間轉化為字串?

先將filetime轉化為systemtime 再 注釋1 對於systemtime的顯示也可以使用mfc中的cstring型別 systemtime st cstring strdate,strtime getlocaltime st strdate.format 4d 2d 2d st.wyea...

如何在程式中將其它視窗調至前台。

像在禁止乙個程式執行多個例項的技巧設計中,當找到已執行的了,程式只是簡單的退出,如果能將這個已經執行的例項調至前台,對於使用者使用來說就更加方便了。對於具有視窗的程式是可以實現的,這個設計同樣可以將正在執行的任意乙個視窗程式調至前台。使用cwnd findwindow 靜態函式獲得視窗指標,就可以進...