20-unix域套接字地址結構
1. unix域協議
看到這個標題,不知道的小夥伴肯定以為這是一個協議族之類的,但實際上unix域協議是在單臺主機上客戶端與服務端之間的通訊方法,簡單來說,unix域協議也是一種程序間通訊方式,用於同一臺主機上的客戶端和服務端,為不同的程序間傳遞描述符。
通常在同一臺主機上,使用unix域套接字通常比TCP套接字效率更高,同時unix域套接字還可以用於在程序間傳遞描述符等等。
那麼unix域套接字是如何在程序間傳遞描述符的?為了弄明白這個問題,我們需要繼續學習unix域協議的其他知識,先從如何構建一個unix域套接字地址結構開始吧。
2. unix域套接字地址結構
unix域套接字的地址結構定義在<sys/un.h>檔案中,具體資訊如下:
struct sockaddr_un {
unsigned short int sun_family; // AF_UNIX或者AF_LOCAL
char sun_path[108]; // 檔案路徑
}
在不同的linux系統中sun_path陣列的大小是不同的,在早期BSDB版本中sun_path陣列大小為108位元組,而POSIX規範中卻沒有定義sun_path陣列的大小,但是我們可以使用sizeof運算子得出sockaddr_un結構體的大小,來驗證這個檔案路徑是否合適存放在sun_path陣列中。
注意:建立的套介面檔案最好為絕對路徑,建議指定在/tmp目錄下,因為POSIX規範對於unix域套接字的相對路徑名將導致未定義行為,這是出於為了方便移植的考慮。另外,存放檔案路徑的sun_path陣列必須以空字元(即’\0’字元)結尾,而sum_family成員用於指定成AF_UNIX或AF_LOCAL。
3. 繫結unix域套接字示例
下面的程式是建立一個unix 域套接字,然後呼叫bind繫結一個檔案路徑名,再呼叫getsockname輸出這個路徑名:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/un.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> int main(int argc , char **argv) { int sockfd; socklen_t len; struct sockaddr_un addr1 , addr2; if(argc != 2){ puts("usage: unixbind <pathname>"); exit(-1); } //指定AF_LOCAL,最後一個引數預設為0 sockfd = socket(AF_LOCAL , SOCK_STREAM , 0); //刪除檔案路徑 unlink(argv[1]); bzero(&addr1 , sizeof(addr1)); //建立unix域套接字 addr1.sun_family = AF_LOCAL; strncpy(addr1.sun_path , argv[1] , sizeof(addr1.sun_path)-1); //繫結unix域套接字 bind(sockfd , (struct sockaddr*)&addr1 , SUN_LEN(&addr1)); //獲取unix域套接字 len = sizeof(addr2); getsockname(sockfd, (struct sockaddr*)&addr2, &len); //列印獲取到的unix與套接字資訊 printf("bound name = %s , returned len = %d\n" , addr2.sun_path , len); return 0; }
分析以上程式碼:
如果sun_path中指定的檔案路徑存在,那麼呼叫bind繫結unix域套接字就會失敗,因此這裡呼叫unlink把檔案路徑刪除掉,如果檔案路徑不存在,則忽略unlink函式返回的錯誤。
使用strncpy函式複製檔案路徑,避免檔案路徑過長導致sun_path溢位,然後初始化sum_path並減1是為了保證sum_path陣列以‘\0’字元結尾。
執行結果:
從上面的執行結果來看,getsockname返回的大小是12,其中sun_family佔2位元組,檔案路徑佔10位元組。通過ls -l命令可以看到檔案型別是S,說明這是一個套接字檔案。