1. 程式人生 > >socket IPC(本地套接字Domain)

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 就可以停止了
      。   使用抽象路徑名的好處   1.因為不會再在檔案系統中建立檔案了,就不需要擔心與檔案系統中已存在的檔案產生名字衝突   2.不需要在使用完套接字之後刪除,當套接字被關閉之後會自動刪除這個抽象名

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;   
}