[Linux][VoIP][IPC] Unix Domain Socket

Socket 是常見的一種 Process之間的溝通方式(IPC, Inter-Porcess Communucation), 

Socket 大致上可分兩種: 1. Internet domain socket    2. Unix domain socket

  • Internet Domain Socket
    • 適用於不同主機間的通訊. Socket 只要知道通訊另一方的 IP和 Port 就可以互相溝通, 這種 Socket是建立在網路傳輸協定之上
  • Unix Domain Socket
    • 用於一台主機的Process之間的溝通方式, 不需要建立在網路協定之上, 主要是基於 file system 發展.
    • 與 Internet domain socket不一定的是, Unix Domain socket 需要知道的是 process 之間需要使用哪一個文件, 基於哪一個文件來做通訊(意旨相同的文件路徑)
    • Unix Domain socket 適用於同一台主機的process之間的通訊, 事實上, Internet domain socket 也可以做到一樣的事情, 透過 lookback IP: 127.0.0.1, 但是 Unix domain socket用於IPC更有效率, 它不需要經過網路協定, 不需要 encode, decode 計算 checksum以及其他傳送封包時候要做的事情, 它只是將一個Process想要傳送的資料複製並傳送給另一個Process
  • Unix domain socket 有 SOCK_DGRAM 或 SOCK_STREAM 兩種工作模式:
    • SOCK_STREAM: 提供一個序列化的連接導向stream, 對應的 protocol為 TCP, 基本上常使用這個模式
    • SOCK_DGRAM: 提供的是一個一個的 datagram, 對應的 protocol 是 UDP
  • Unix domain socket 是全雙工運作, 下圖為 SOCK_STREAM為例的工作流程:
  • 舉例: Client端 輸入 command, Server端 收到 command 後, 回應訊息
  • Client 端: 
    • 假設 Client 端輸入一個指令讓電話撥出 dial tone   ~# sip_config StartTones 0 0
    • sip_config.c  int main()
      • socket(PF_LOCAL, SOCK_STREAM, 0)
      • #define socket_path  "/tmp/socket"
      • name.sun_family = AF_LOCAL; strcpy(name.sun_path, socket_path);
      • connect(socket_fd, &name, SUN_LEN(&name))
      • write(socket_fd, buf, sizeof(buf))
    • sip_config 是已經 compile 好的一個執行檔, 也就是透過 sip_config 做連線的動作, 並且將會面的訊息傳遞給 Server 端
  • Server 端: 
    • sip_core.c > pthread_create(SIP_CORE_ProcUI) > 建 socket 等 client 的 command request
      • socket(PF_LOCAL, SOCK_STREAM, 0)
      • name.sun_family = AF_LOCAL
      • #define socket_path  "/tmp/socket"
      • strcpy(name.sun_path, socket_path)
      • bind(s, (struct sockaddr *)&name, SUN_LEN(&name))
      • listen(s, num_client);   // #define num_client 6
      • 利用  while(1) + select 等訊息
      • FD_ZERO() > FD_SET
      • ret = select(AdminSocket + 1, &ibits, 0, 0, &timeout)
      • ret > 0 → FD_ISSET(AdminSocket, &ibits)
      • accept(AdminSocket, (struct sockaddr *)&client_name, &client_name_len)
      • read(client_socket_fd, buffer, sizeof(buffer))
      • close(client_socket_fd)
  • 常用 function 說明
    • int socket (int domain, int type, int protocol)
      • 建立一個 socket file descriptor, socket 相關的function, 操作上都需要有一個 socket file descriptor才能針對特定的 domain socket 做處理
      • domain: Internet domain: AF_INET or AF_INET6  Unix domain: AF_LOCAL
      • type: SOCK_STREAM (tcp),  SOCK_DGRAM(udp)
      • protocol: IPPROTO_TCP, IPPROTO_UDP, 通常會設定為0, kernel 選擇 type 對應的 default protocol
    • int bind (int sockfd, struct sockaddr *local_addr, socklen_t addrlen);
      • 把一個特定的位址(IP address, Port) 綁定給 socket
    • int listen(int sockfd, int backlog)
      • sockfd: 想要設定的 socket file descriptor
      • backlog: 設定最多能有幾個人可以連到 server
    • int accpet(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
      • Server listen 到 Client 的 Request之後, 使用 accept()接收Request, 這樣連結就建立完成了.
      • accept()被使用時, 它會為該Request產生出一個新的SOCKET, 並把這個Request從 listen queue剔除
    • int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
      • client 使用 connect() 跟 server 連接
      • struct sockaddr *serv_addr: 儲存要連接的server資訊
    • ssize_t write(int fd, const void *buf, size_t count)
      • 把資料寫到特定的 file descriptor
      • *buf: 想要寫入的資料
    • ssize_t read(int fd, void *buf, size_t count);
      • 從特定的 file descriptor讀取資料
    • int select(int maxfds, fd_set *readfds, fd_set *writefds, fd_set errorfds, struct timeval *timeout)
      • maxfds: 是一個整數值, 指集合中所有file decriptor的範圍, 意即所有 file descriptor的最大值加1
  • 常用的 UDP Data gram 傳輸流程