熟悉 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 程序中,fd
0 = 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 靜態函式獲得視窗指標,就可以進...