使用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
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #define UNIX_DOMAIN "/tmp/UNIX.domain"
- int main(void)
- {
- socklen_t clt_addr_len;
- int listen_fd;
- int com_fd;
- int ret;
- int i;
- static char recv_buf[1024];
- int len;
- struct sockaddr_un clt_addr;
- struct sockaddr_un srv_addr;
- listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
- if(listen_fd<0)
- {
- perror("cannot create communication socket");
- return 1;
- }
- //set server addr_param
- srv_addr.sun_family=AF_UNIX;
- strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);
- unlink(UNIX_DOMAIN);
- //bind sockfd & addr
- ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
- if(ret==-1)
- {
- perror("cannot bind server socket");
- close(listen_fd);
- unlink(UNIX_DOMAIN);
- return 1;
- }
- //listen sockfd
- ret=listen(listen_fd,1);
- if(ret==-1)
- {
- perror("cannot listen the client connect request");
- close(listen_fd);
- unlink(UNIX_DOMAIN);
- return 1;
- }
- //have connect request use accept
- len=sizeof(clt_addr);
- com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
- if(com_fd<0)
- {
- perror("cannot accept client connect request");
- close(listen_fd);
- unlink(UNIX_DOMAIN);
- return 1;
- }
- //read and printf sent client info
- printf("/n=====info=====/n");
- for(i=0;i<4;i++)
- {
- memset(recv_buf,0,1024);
- int num=read(com_fd,recv_buf,sizeof(recv_buf));
- printf("Message from client (%d)) :%s/n",num,recv_buf);
- }
- close(com_fd);
- close(listen_fd);
- unlink(UNIX_DOMAIN);
- return 0;
- }
使用套接字在UNIX域內實現進程間通信的客戶端程序。相比服務端的程序,客戶段較為簡單。程序首先通過調用socket函數創建通信所需的套接字,然后,調用connect函數來連接服務器,在成功建立連接后,通過調用write函數向服務器發送指定的消息。
client代碼:u_unix.c
- //c_unix.c
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #define UNIX_DOMAIN "/tmp/UNIX.domain"
- int main(void)
- {
- int connect_fd;
- int ret;
- char snd_buf[1024];
- int i;
- static struct sockaddr_un srv_addr;
- //creat unix socket
- connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
- if(connect_fd<0)
- {
- perror("cannot create communication socket");
- return 1;
- }
- srv_addr.sun_family=AF_UNIX;
- strcpy(srv_addr.sun_path,UNIX_DOMAIN);
- //connect server
- ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
- if(ret==-1)
- {
- perror("cannot connect to the server");
- close(connect_fd);
- return 1;
- }
- memset(snd_buf,0,1024);
- strcpy(snd_buf,"message from client");
- //send info server
- for(i=0;i<4;i++)
- write(connect_fd,snd_buf,sizeof(snd_buf));
- close(connect_fd);
- return 0;
- }
編譯運行:
[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]#