Linux的socketpair()函數,是創建一對互相連接著的socket描述符。
類似TCP連接,兩個文件描述符都可以讀寫,sv[0]寫入的數據在sv[1]讀出,sv[1]寫入的數據在sv[0]讀出。
它的參數與socket()類似,只是多了一個int sv[2]項用於返回這兩個文件描述符,返回值用於提示創建是否成功。
而socket()函數的返回值就是文件描述符,用返回-1提示失敗。
上圖為它的man手冊。
文件描述符,屬於進程的資源之一,與進程的變量、代碼類似,都會在fork()時被父子進程共享。
所以,socketpair()這個函數就被master+worker型的多進程伺服器廣泛用於master和各個worker的通信。
也可以用於子進程間的通信,因為第二個子進程被fork()時一樣要共享父進程的socketpair,只要子進程間規定好誰使用1,誰使用2就行。
文件描述符在內核裡對應著struct file結構,文件讀寫API最終都要處理內核的文件管理結構,文件描述符是這個結構的代號。
只要把某個文件的管理結構映射到某幾個進程,他們就可以共享同一個文件了,在程序裡的表現就是他們共享了同一對socketpair。
具體用法見如下三張圖:
1,首先包含頭文件「sys/types.h」和「sys/socket.h」,
2,然後在main()函數裡先創建socketpair,然後再fork()子進程,這樣子進程就共享了父進程的socketpair,
3,根據fork的返回值去判斷出錯、子進程、還是父進程,然後寫不同的執行代碼。
因為是進程間通信,所以使用AF_UNIX域(domain)的socket,類型選擇SOCK_STREAM,類似TCP的可靠連結。
不要選SOCK_DGRAM,類似udp會在傳輸時丟包的。
我們在父進程裡fork了第二個子進程,讓這兩個子進程互相通信,一個使用sv[1]發送,一個使用sv[0]接收。
他們把各自不用的那個sv[i]關掉,只保留用的那個。
發送字符串時帶著結尾的』\0』,6個字符,因為strlen不計算結尾的』\0』,所以發送長度要加1。
這裡發送數據使用了socket的send()和recv()函數,阻塞式調用。
他們也可以配合epoll()機制進行非阻塞式的調用,是nginx伺服器的底層基礎。
運行結果圖:
PS:一個進程也可以在AF_UNIX域的socket上顯式監聽一個字符串表示的文件目錄,等待其他進程去連接它,類似監聽tcp的socket一樣。
AF_UNIX的socketpair還可以把一個進程打開的文件,傳遞給另一個進程,這叫傳遞socket的附加數據,具體用法可以man一下cmsg。在Linux內核裡就是把這個文件的管理結構struct file的指針加到目標進程的文件列表裡。