IPC: (3) socket

Socket 為 Linux IPC (Inter-process communication) 的方式之一. process 可藉由 socket 傳遞資料至另一個 process. 以下以 AF_INET 作為範例來介紹 TCP 與 UDP.
TCP 與 UDP 皆以講解流程圖與 API 的用法為主. 完整範例 code 請點選 github 連結.

TCP

以下為 TCP server 與 client 端的建立通訊流程. 當中的 write 與 read 可以與 send 與 recv 替換. 範例 code 請參閱 github

TCP 流程圖 [1]

Server 與 client 透過 socket 來通訊的流程如下.

  • Server 端:
  • 建立 socket 後, 藉由 bind() 綁上 ip 與 port. 執行 listen() 等待 client 的連線要求. 透過 accept() 接受要求後, 便可開始與 client 進行傳輸的動作.

  • Client 端:
  • 建立 socket 後, 在 connect() 綁上 server 的 ip 與 port 進行連線. 當連線要求被接受後, 便可以進行傳輸.

TCP APIs

socket [2]

#include <sys/socket.h>
sockfd = socket(int socket_family, int socket_type, int protocol);

socket_family: 為所用的 socket domain, 這邊採用網際網路的關係, 故選用 AF_INET.
socket_type: 由於選用 TCP, 所以使用 SOCK_STREAM.
protocol: 預設數值為 0

bind [3]

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}

sockfd: 為所要用的 socket fd.
addr: 設定此 socket 所要綁定的 ip 與 port.
addrlen: 為 addr 的大小

在撰寫的時候, 通常都是用 sockaddr_in 資料結構來填寫 ip 與 port. 之後再透過資料轉換的方式轉成 bind() 所需的格式. 詳細的解說可參考[9]

listen [4]

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

sockfd: 所要進行 listen 的 socket fd. 其類型須為 SOCK_STREAM 或 SOCK_SEQPACKET.
backlog: 在 queue 當中, 等待進行處理的 connection 數量.

accept [5]

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd: 正在進行 listen 的 socket fd.
addr: 儲存 remote 端的 ip address 與 port.
addrlen: addr 的大小.

connect [6]

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);

sockfd: 所建立的 socket fd.
addr: 所要連接的 server ip 與 port 資訊.
addrlen: addr 的大小

UDP

底下為 UDP server 與 client 端的建立通訊流程, 範例 code 請參閱 github

UDP 流程圖 [1]

Server 與 client 建立 socket 來通訊的流程如下圖

  • Server 端:
  • 建立 socket 後, 藉由 bind() 綁上 ip 與 port. 便可以開始接收(recvfrom())與傳輸(sendto())資料. 其中傳送者的資訊可以在 packet 的標頭得知, 不用擔心資料不知道要回傳資料給誰.

  • Client 端:
  • 建立 socket 後, 直接在 sendto() 帶入 server 的 ip 與 port, 便可以把資料直接傳遞過去.

UDP APIs

socket [2]

#include <sys/socket.h>
sockfd = socket(int socket_family, int socket_type, int protocol);

socket_family: 為所用的 socket domain, 這邊採用網際網路的關係, 故選用 AF_INET.
socket_type: 由於選用 UDP, 所以使用 SOCK_DGRAM.
protocol: 預設數值為 0

bind [3]

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}

sockfd: 為所要用的 socket fd.
addr: 設定此 socket 所要綁定的 ip 與 port.
addrlen: 為 addr 的大小

在撰寫的時候, 通常都是用 sockaddr_in 資料結構來填寫 ip 與 port. 之後再透過資料轉換的方式轉成 bind() 所需的格式. 詳細的解說可參考[9]

recvfrom [7]

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

sockfd: bind 所使用的 socket fd.
buf: 存在資料的 address
len: buf 的大小
flags: 預設為 0
src_addr: 存在 remote 端的資訊.
addrlen: src_addr 的大小

recvfrom [8]

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd: process 所使用的 socket fd.
buf: 存在資料的 address
len: buf 的大小
flags: 預設為 0
src_addr: 存在 remote 端的資訊.
addrlen: src_addr 的大小

Reference

  1. NETWORK PROGRAMMING LINUX SOCKET PART 2: THE SERVER SIDE ISSUES
  2. socket(7)
  3. bind(2)
  4. listen(2)
  5. accept(2)
  6. connect(2)
  7. recvfrom(2)
  8. sendto(2)
  9. sockaddr和sockaddr_in的区别

留言

這個網誌中的熱門文章

yocto recipe : (1) 撰寫 recipe

yocto recipe : (2) 撰寫 bbappend

yocto recipe : (3) 使用 External Source 來編譯軟體