TCP套接字編寫,多進程多線程版本 Linux網絡通信( 二 )


網絡數據流的地址應該這樣規定:先發出的數據是低地址,后發出的數據是高地址 。

  • 大端字節序: 高位存放在低地址,低位存放在高地址
  • 小端字節序: 低位存放在低地址,高位存放在高地址

TCP套接字編寫,多進程多線程版本 Linux網絡通信

文章插圖
如果雙方主機的數據在內存存儲的字節序不同,就會造成接收方收到的數據出現偏差,所以為了解決這個問題,又有了下面的規定:
  • TCP/IP協議規定,網絡數據流采用大端字節序,不管這臺主機是大端機還是小端機, 都會按照這個TCP/IP規定的網絡字節序來發送/接收數據
  • 所以如果發送的主機是小端機,就需要把要發送的數據先轉為大端,再進行發送,如果是大端,就可以直接進行發送 。
為了方便我們進行網絡程序的代碼編寫,有下面幾個API提供給我們用來做網絡字節序和主機字節序的轉換 , 如下:
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);說明:
  • h代表的是host,n代表的是network,s代表的是16位的短整型 , l代表的是32位長整形
  • 如果主機是小端字節序,函數會對參數進行處理,進行大小端轉換
  • 如果主機是大端字節序,函數不會對這些參數處理,直接返回
注意:在編程中我們需要自行進行大小端轉化的就只有三個:ip地址,傳輸數據和端口,這兩個數據需要我們進行大端的轉化,其他的在計算機組包的時候會自動給我們轉化 。
Socket常見的API常用的有以下幾個,后面會具體的介紹
// 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)int socket(int domain, int type, int protocol);// 綁定端口號 (TCP/UDP, 服務器)int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 開始監聽socket (TCP, 服務器)int listen(int socket, int backlog);// 接收請求 (TCP, 服務器)int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立連接 (TCP, 客戶端)int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);Sockaddr結構體
TCP套接字編寫,多進程多線程版本 Linux網絡通信

文章插圖
  • sockaddr_in用來進行網絡通信,sockaddr_un結構體用來進行本地通信
  • sockaddr_in結構體存儲了協議家族 , 端口號,IP等信息,網絡通信時可以通過這個結構體把自己的信息發送給對方 , 也可以通過這個結構體獲取遠端的這些信息
  • 可以看出,這三個結構體的前16位時一樣的,代表的是協議家族,可以根據這個參數判斷需要進行哪種通信(本地和跨網絡)
  • IPv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結構體表示,包括16位地址類型, 16位端口號和32位IP地址;而IPv6地址用sockaddr_in6結構體來表示
  • IPv4、 IPv6地址類型分別定義為常數AF_INET、 AF_INET6 。這樣 , 只要取得某種sockaddr結構體的首地址,不需要知道具體是哪種類型的sockaddr結構體,就可以根據地址類型字段確定結構體中的內容
  • socket API可以都用struct sockaddr *類型表示 , 在使用的時候需要強制轉化成sockaddr;這樣的好處是程序的通用性,可以接收IPv4,IPv6,以及UNIX Domain Socket各種類型的sockaddr結構體指針為參數
注意:IPv4和IPv6分別有自己對應的結構體 , 但是為了統一 , 我們不知道用戶要傳的是ipv4還是ipv6,所以就類似于我們不知道用戶要輸入char還是int類型,此時我們就會寫成void *類型;同理 , 為了統一,這里有個通用的套接字結構體struct sockaddr,將結構體IPv4和IPv6轉化成sockaddr類型就可以了,struct sockaddr會根據ipv4和ipv6結構體的前幾位判斷需要傳輸的協議類型是IPv4還是IPv6 。
sockaddr_in的結構: 因為我們主要用到網絡通信,所以這里主要介紹這個結構體,打開/usr/include/linux/in.h
TCP套接字編寫,多進程多線程版本 Linux網絡通信

文章插圖
sin_family代表的是地址類型,我們主要用的是AF_INET , sin_port代表的是端口號,sin_addr代表的是網絡地址 , 也就是IP地址,用了一個結構體struct in_addr進行描述
struct in_addr{ _be32 a_addr;}這里填充的就是IPv4的地址 , 一個32位的整數
地址轉換函數IP地址可以用點分十進制的字符串(例如127.0.0.1),這里涉及到字符串和32位整網絡的大端數據之間的相互轉換 。下面價紹二者之間轉化的庫函數:

推薦閱讀