使用socket實現處理序間通訊:(UNIX domain中連線導向通訊)
使用通訊端除了可以實現網路間不同主機間的通訊外,還可以實現同一主機的不同進程間的通訊,且建立的通訊是雙向的通訊。
man unix內容如下:
NAME( 名稱)
unix, PF_UNIX, AF_UNIX, PF_LOCAL, AF_LOCAL ? 用於本地內部進程通訊的套接 字。
SYNOPSIS( 總覽 )
#include <sys/socket.h>
#include <sys/un.h>
unix_socket = socket(PF_UNIX, type, 0);
error = socketpair(PF_UNIX, type, 0, int *sv);
DESCRIPTION( 描述 )
PF_UNIX (也稱作 PF_LOCAL ) 通訊端族用來在同一機器上的提供有效進程間通訊.Unix 通訊端可以是匿名的(由 socketpair(2)建立), 也可以與通訊端類型檔案相關聯. Linux 還支援一種抽象名字空間, 它是獨立於檔案系統的.
有效類型有: SOCK_STREAM 用於面向流的通訊端, SOCK_DGRAM 用於面向資料報的通訊端,其可以儲存訊息界限. Unix 通訊端總是可靠的,而且不會重組資料報.
Unix 通訊端支援把檔案描述符或者進程的信用證明作為資料報的輔助資料傳遞給 其它進程.
ADDRESS FORMAT( 地址格式 )
unix 地址定義為檔案系統中的一個檔案名稱或者抽象名字空間中的一個單獨的字串. 由 socketpair(2) 建立的通訊端是匿名的.對於非匿名的通訊端,目標地址 可使用 connect(2) 設定. 本地地址可使用 bind(2) 設定. 當通訊端串連上而且它沒有一個本地地址時, 會自動在抽象名字空間中產生一個唯一的地址.
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路徑名 */
};
sun_family 總是包含 AF_UNIX. sun_path 包含空零結尾的通訊端在檔案系統中的路徑名. 如果 sun_path 以空零位元組開頭,它指向由 Unix 協議模組維護的抽象名字空間. 該通訊端在此名字空間中的地址由 sun_path 中的剩餘位元組給定. 注意抽象名字空間的名字都不是空零終止的.
SOCKET OPTIONS( 通訊端選項 )
由 於 曆 史 原 因, 這些通訊端選項通過 SOL_SOCKET 類型確定, 即使它們是 PF_UNIX 指定的. 它們可以由 setsockopt(2) 設定. 通過指定 SOL_SOCKET 作 為通訊端族用 getsockopt(2) 來讀取.
SO_PASSCRED 允許接收進程輔助資訊發送的信用證明. 當設定了該選項且通訊端 尚未串連時, 則會自動產生一個抽象名字空間的唯一名字. 值為一個整數布爾標 識.
ANCILLARY MESSAGES( 輔助資訊 )
由 於 曆 史 原 因, 這些輔助資訊類型通過 SOL_SOCKET 類型確定, 即使它們是 PF_UNIX 指定的. 要發送它們, 可設定結構 cmsghdr 的 cmsg_level 字 段 為 SOL_SOCKET, 並 設 置 cmsg_type 欄位為其類型. 要獲得更多資訊, 請參看 cmsg(3).
SCM_RIGHTS
為其他進程發送或接收一套開啟檔案描述符. 其資料部分包含一個檔案 描述符的整型數組. 已傳檔案描述符的效果就如它們已由 dup(2) 建立 過一樣.
SCM_CREDENTIALS
發送或者接收 unix 信用證明. 可用作認證.信用證明傳送 以 struct ucred 輔助資訊的形式傳送.
struct ucred {
pid_t pid; /* 發送進程的進程標識 */
uid_t uid; /* 發送進程的使用者標識 */
gid_t gid; /* 發送進程的組標識 */
};
發 送者確定的信用證明由核心檢查. 一個帶有有效使用者標識 0 的進程允許指定 不與其自身值相匹配的值.寄件者必須確定其自身的進程 標 識( 除非它帶 有 CAP_SYS_ADMIN), 其 用 戶 標識,有效使用者標識或者設定使用者標識(除非它帶有CAP_SETUID), 以及其組標識,有效組標識或者 設 置 組 標 識( 除非它帶有CAP_SETGID). 為 了 接 收 一 條 struct ucred 訊息,必須在通訊端上啟用 SO_PASSCRED 選項.
ERRORS( 錯誤 )
ENOMEM
記憶體溢出.
ECONNREFUSED
connect(2) 調用了一個未在監聽的通訊端對象. 這可能發生在遠程套 接字不存在或者檔案名稱不是通訊端的時候.
EINVAL
傳遞了無效參數. 通常的產生原因是已傳地址的 sun_type 字 段的 AF_UNIX 設定丟失, 或者通訊端對應用的操作處於無效狀態.
EOPNOTSUPP
在非面向流的通訊端上調用了流操作,或者試圖使用出界的資料選項.
EPROTONOSUPPORT
傳遞的協議是非 PF_UNIX 的.
ESOCKTNOSUPPORT
未知的通訊端類型.
EPROTOTYPE
遠程通訊端與本地通訊端類型不符 (SOCK_DGRAM 對SOCK_STREAM).
EADDRINUSE
選擇的本地地址已經佔用,或者檔案系統通訊端對象已經存在.
EISCONN
在 一個已經串連的通訊端上調用 connect(2) 或者指定的目標地址在一 個已串連的通訊端上.
ENOTCONN
通訊端操作需要一個目的地址,但是通訊端尚未串連.
ECONNRESET
遠程通訊端意外關閉.
EPIPE
遠程通訊端在一個流通訊端上關閉了.如果啟用,會同時發送一個 SIGPIPE 標識.這可以通過傳遞 MSG_NOSIGNAL 標識給 sendmsg(2) 或者 recvmsg(2) 來避免.
EFAULT
使用者記憶體位址無效.
EPERM
寄件者在 struct ucred 中傳遞無效的信用證明.
當產生一個檔案系統通訊端對象時, 可能會由通用套接層或者檔案系統產生其它錯誤. 要獲得更多資訊,可參見合適的手冊頁.
實踐:
使用通訊端在UNIX域內實現處理序間通訊的服務端程式。首先,程式通過調用socket函數,建立了監聽串連的通訊端,然後調用bind函數,將通訊端與地址資訊關聯起來。調用listen函數實現對該連接埠的監聽,當有串連請求時,通過調用accept函數建立與客戶機的串連,最後,調用read函數來讀取客戶機發送過來的訊息,當然也可以使用recv函數實現相同的功能。
server代碼:s_unix.c
//s_unix.c<br />#include <stdio.h><br />#include <sys/types.h><br />#include <sys/socket.h><br />#include <sys/un.h><br />#define UNIX_DOMAIN "/tmp/UNIX.domain"<br />int main(void)<br />{<br /> socklen_t clt_addr_len;<br /> int listen_fd;<br /> int com_fd;<br /> int ret;<br /> int i;<br /> static char recv_buf[1024];<br /> int len;<br /> struct sockaddr_un clt_addr;<br /> struct sockaddr_un srv_addr;<br /> listen_fd=socket(PF_UNIX,SOCK_STREAM,0);<br /> if(listen_fd<0)<br /> {<br /> perror("cannot create communication socket");<br /> return 1;<br /> } </p><p> //set server addr_param<br /> srv_addr.sun_family=AF_UNIX;<br /> strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);<br /> unlink(UNIX_DOMAIN);<br /> //bind sockfd & addr<br /> ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));<br /> if(ret==-1)<br /> {<br /> perror("cannot bind server socket");<br /> close(listen_fd);<br /> unlink(UNIX_DOMAIN);<br /> return 1;<br /> }<br /> //listen sockfd<br /> ret=listen(listen_fd,1);<br /> if(ret==-1)<br /> {<br /> perror("cannot listen the client connect request");<br /> close(listen_fd);<br /> unlink(UNIX_DOMAIN);<br /> return 1;<br /> }<br /> //have connect request use accept<br /> len=sizeof(clt_addr);<br /> com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);<br /> if(com_fd<0)<br /> {<br /> perror("cannot accept client connect request");<br /> close(listen_fd);<br /> unlink(UNIX_DOMAIN);<br /> return 1;<br /> }<br /> //read and printf sent client info<br /> printf("/n=====info=====/n");<br /> for(i=0;i<4;i++)<br /> {<br /> memset(recv_buf,0,1024);<br /> int num=read(com_fd,recv_buf,sizeof(recv_buf));<br /> printf("Message from client (%d)) :%s/n",num,recv_buf);<br /> }<br /> close(com_fd);<br /> close(listen_fd);<br /> unlink(UNIX_DOMAIN);<br /> return 0;<br />}<br />
使用通訊端在UNIX域內實現處理序間通訊的用戶端程式。相比服務端的程式,客戶段較為簡單。程式首先通過調用socket函數建立通訊所需的通訊端,然後,調用connect函數來串連伺服器,在成功建立串連後,通過調用write函數向伺服器發送指定的訊息。
client代碼:u_unix.c
//c_unix.c<br />#include <stdio.h><br />#include <sys/types.h><br />#include <sys/socket.h><br />#include <sys/un.h><br />#define UNIX_DOMAIN "/tmp/UNIX.domain"<br />int main(void)<br />{<br /> int connect_fd;<br /> int ret;<br /> char snd_buf[1024];<br /> int i;<br /> static struct sockaddr_un srv_addr;<br />//creat unix socket<br /> connect_fd=socket(PF_UNIX,SOCK_STREAM,0);<br /> if(connect_fd<0)<br /> {<br /> perror("cannot create communication socket");<br /> return 1;<br /> }<br /> srv_addr.sun_family=AF_UNIX;<br /> strcpy(srv_addr.sun_path,UNIX_DOMAIN);<br />//connect server<br /> ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));<br /> if(ret==-1)<br /> {<br /> perror("cannot connect to the server");<br /> close(connect_fd);<br /> return 1;<br /> }<br /> memset(snd_buf,0,1024);<br /> strcpy(snd_buf,"message from client");<br />//send info server<br /> for(i=0;i<4;i++)<br /> write(connect_fd,snd_buf,sizeof(snd_buf));<br /> close(connect_fd);<br /> return 0;<br />}</p><p>
編譯運行:
[root@localhost liuxltest]# ./s_unix &
[1] 11664
[root@localhost liuxltest]# ./c_unix &
=====info=====
Message from client (1024)) :message from client
Message from client (1024)) :message from client
Message from client (1024)) :message from client
Message from client (1024)) :message from client
[2] 11665
[1]- Done ./s_unix
[2]+ Done ./c_unix
[root@localhost liuxltest]#
當運行s_unix程式後,該程式將處於監聽狀態。這時,可以通過netstat命令查看程式運行情況,s_unix的通訊端類型為流通訊端,並處於監聽狀態。
[root@localhost liuxltest]#
[root@localhost liuxltest]# ./s_unix &
[1] 12056
[root@localhost liuxltest]# netstat -an |grep /tmp
unix 2 [ ACC ] STREAM LISTENING 64014 /tmp/ssh-CekOJ11069/agent.11069
unix 2 [ ACC ] STREAM LISTENING 6216 /tmp/.font-unix/fs7100
unix 2 [ ACC ] STREAM LISTENING 62042 /tmp/ssh-XOCgkr9439/agent.9439
unix 2 [ ACC ] STREAM LISTENING 62316 /tmp/ssh-mojoaQ9648/agent.9648
unix 2 [ ACC ] STREAM LISTENING 65267 /tmp/UNIX.domain
unix 2 [ ACC ] STREAM LISTENING 65210 /tmp/ssh-NlKtA12012/agent.12012
[root@localhost liuxltest]#