socket IPC(本地套接字Domain)
UNIX Domain Socket的特點
1.UNIX Domain Socket為什麼比TCP/IP在本地上通訊更加快 因為UNIX Domain Socket不經過網路協議棧 / 不需要打包拆包 / 計算校驗和 / 維護序號和應答,只是將應用層資料從一個程序拷貝到另一個程序 2.UNIX Domain Socket也提供面向流和麵向資料包的兩種API介面,類似於TCP和UDP,但是面向訊息的 UNIX Domain Socket也是可靠的,訊息既不會丟失也不會失序。 3.全雙工 4.目前UNIX Domain Socket已經成為最廣泛的IPC機制
Unix 本地套接字 API
1.socket建立套接字
mysocket = socket(int socket_family, int socket_type, int protocol)
網路socket | UNIX Domain Socket | |
---|---|---|
address family | AF_INET或PF_INET | AF_UNIX |
地址格式不同 | sockaddr_in(IP+Port) | sockaddr_un(本地檔案) |
type | SOCK_STREAM、SOCK_DGRAM 和 SOCK_RAW | SOCK_STREAM、SOCK_DGRAM 和 SOCK_RAW |
Protocol | 0 | 0 |
2.繫結地址bind
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); my_addr引數
- struct sockaddr*
- struct sockaddr_in*
- struct sockaddr_un*
對於 Unix 本地套接字來說,繫結的地址就不是原來的“IP地址 + 埠號”了,而是一個有效的路徑。
結論:Unix 本地套接字的地址結構體中包含兩個成員,其中 sun_family 表示協議族,填 AF_LOCAL 或 AF_UNIX 即可;sun_path 表示一個路徑名。
補充:路徑名,其實還要分為兩種,普通路徑名 / 抽象路徑名。
- 普通路徑名是一個正常的字串,也就是說,sun_path 欄位是以空字元(’\0’)結尾的。
- 而抽象路徑名,sun_path 欄位的第一個位元組需要設定成 NULL(’\0’),所以
在計算抽象路徑名的長度的時候就要特別小心了,否則在解析抽象路徑名時就有可能出現異常情況,因為抽象路徑名不是像解析普通路徑名那樣,解析到第一個 NULL 就可以停止了
3.其他API
其他的一些 API,比如 listen()、accept()、connect(),以及資料通訊用的 read()、write()、recv()、send()、recvfrom()、sendto()、recvmsg()、sendmsg(),用法跟網路 socket 基本一樣,主要是地址結構體需要注意一下
4.常用API
1)用於建立類似於無名管道(pipe)的本地套接字
int socketpair(int domain, int type, int protocol, int sv[2]);
2)當不再需要這個 Unix 域套接字時,應刪除路徑名對應的檔案
int unlink(const char *pathname); int remove(const char *pathname); 注意,如果是抽象路徑名,就不需要在使用完本地套接字後手動刪除對應的套接字檔案,因為當本地套接字被關閉之後,核心會自動刪除這個抽象名。
3)獲取本地套接字的地址資訊
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
TCP(位元組流套接字)
【unixstr_serv.c】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIXSTR_PATH "/tmp/unix.str"
#define LISTENQ 5
#define BUFFER_SIZE 256
int main(void)
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_un servaddr, cliaddr;
if(-1 == (listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)))
{
perror("socket");
exit(EXIT_FAILURE);
}
unlink(UNIXSTR_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
if(-1 == bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))
{
perror("bind");
exit(EXIT_FAILURE);
}
listen(listenfd, LISTENQ);
len = sizeof(cliaddr);
if(-1 == (connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len)))
{
perror("accept");
exit(EXIT_FAILURE);
}
char buf[BUFFER_SIZE];
while(1)
{
bzero(buf, sizeof(buf));
if(read(connfd, buf, BUFFER_SIZE) == 0) break;
printf("Receive: %s", buf);
}
close(listenfd);
close(connfd);
unlink(UNIXSTR_PATH);
return 0;
}
【unixstr_cli.c】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIXSTR_PATH "/tmp/unix.str"
#define LISTENQ 5
#define BUFFER_SIZE 256
int main(void)
{
int sockfd;
struct sockaddr_un servaddr;
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
char buf[BUFFER_SIZE];
while(1)
{
bzero(buf, sizeof(BUFFER_SIZE));
printf(">> ");
if(fgets(buf, BUFFER_SIZE, stdin) == NULL)
{
break;
}
write(sockfd, buf, strlen(buf));
}
close(sockfd);
return 0;
}
UDP(資料報套接字)
類似於上面 TCP 的例子,我們使用絕對路徑名”/tmp/unix.dg”來實現一個數據報的本地套接字,一端接收資料,一端傳送資料。 【unixdg_serv.c】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIXDG_PATH "/tmp/unix.dg"
#define BUFFER_SIZE 256
int main(void)
{
int sockfd;
struct sockaddr_un servaddr, cliaddr;
sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
unlink(UNIXDG_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
socklen_t len = sizeof(cliaddr);
char buf[BUFFER_SIZE];
while(1)
{
bzero(buf, BUFFER_SIZE);
if(0 == recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, &len))
{
break;
}
printf("recvfrom: %s", buf);
}
close(sockfd);
unlink(UNIXDG_PATH);
return 0;
}
【unixdg_cli.c】
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIXDG_PATH "/tmp/unix.dg"
#define BUFFER_SIZE 256
int main(void)
{
int sockfd;
struct sockaddr_un servaddr, cliaddr;
socklen_t len;
sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
/* local address */
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, UNIXDG_PATH);
bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
/* remote address */
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
len = sizeof(servaddr);
char buf[BUFFER_SIZE];
while(1)
{
bzero(buf, BUFFER_SIZE);
printf(">> ");
if(fgets(buf, BUFFER_SIZE, stdin) == NULL)
{
break;
}
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, len);
}
close(sockfd);
return 0;
}