1. 程式人生 > >linux網路程式設計總結(1)

linux網路程式設計總結(1)

網路程式設計就是通過網路協議實現計算機間的通訊。那麼就有兩個問題,如何精準的定位主機和如何進行高效的資料傳輸。

IP地址來標識網路中的主機,埠號用來定位主機中的某個程序。通過IP和埠號可以唯一確定網路的某臺計算機中的程序。

套接字

即socket,用於描述IP地址和埠,是一個通訊的控制代碼,程序利用套接字向網路發起請求或回覆請求。

套接字的工作原理

要實現網路間的通訊,至少需要一對套接字,一個運行於客戶端,一個運行於服務端。套接字的連線過程可以分為,

1 服務端監聽   2 客戶端連線請求   3 連線請求確認

服務端監聽:服務端並不定位到具體的客戶端套接字,而是處於等待連線狀態,實時監聽網路的狀態

連線請求:即客戶端套接字向服務端發起連線請求,目標是服務端的套接字,為此客戶端要描述服務端的地址和埠號,然後想服務端套接字發起連線請求。

連線確認;即服務端接收到客戶端的連線請求之後,建立一個新的執行緒與客戶端套接字建立連線,並向客戶端回覆服務端的地址和埠資訊,客戶端確認了此連線,該連線就建立好了,而服務端繼續處於監聽狀態,監聽其他套接字發起的請求。

套接字地址結構

struct in_addr {
    in_addr_t  s_addr;        // 32-bit IPv4 address
                        //network byte ordered
}
struct sockaddr_in {
    sa_family_t  sin_family;        //AF_INET
    in_port_t    sin_port;            //16-bit TCP or UDP port nummber, network byte ordered
    struct in_addr    sin_addr;            //32-bit IPv4 address, network byte ordered
    char     sin_zero[8];            //unused
}

  sockaddr_in是網路套接字地址結構,大小為16位元組,定義在<netinet/in>標頭檔案中,一般我們在程式中是使用該結構體,但是作為引數傳遞給套接字函式時需要強轉為sockaddr型別注意該結構體中port和addr成員是網路序的(大端結構)

struct sockaddr {
    sa_family_t  sa_family;            //address family: AF_XXX value
    char        sa_data[14];            //protocol-specific address
}

  sockaddr是通過套接字地址結構,當作為引數傳遞給套接字函式時,套接字地址結構總是以指標方式來使用,比如bind/accept/connect函式等

網路位元組序轉換在此不多做介紹了。

地址轉換函式:inet_ntop();    inet_pton();

#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); // 返回:成功為1,輸入不是有效表示式返回0,出錯為-1
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len); // 返回:成功為指向結果的指標,出錯為NULL

  這兩個函式對於IPv4和IPv6都適用,p代表表示式(presentation)、n表示數值(numeric)。第一個函式嘗試轉化由strptr指標所指的字串,通過addptr指標存放二進位制結果,成功返回1,如果對指定的family而言輸入的不是有效的表達格式,那麼返回0

  inet_ntop進行相反的操作,如果len的值太小,不足以存放表示式結果,則返回一個空指標,並置error為ENOSPC。inet_ntop函式的strptr引數不可以是一個空指標,呼叫者必須為目標儲存單元分配記憶體並制定其大小,呼叫成功時,這個指標就是該函式返回值。

SOCKET函式  int socket(int domain, int type, int protocol);

為了執行網路IO,第一件事就是呼叫SOCKET,建立套接字描述符,指定期望的通訊協議族(比如IPv4、IPv6)和套接字字型別(位元組流、資料報或原始套接字)。

family的值有:

  • AF_INET IPv4協議
  • AF_INET6 Ipv6協議
  • AF_LOCAL Unix協議域
  • AF_ROUTE 路由套接字
  • AF_KEY 祕鑰套接字

type的值有:

  • SOCK_STREAM 位元組流套接字
  • SOCK_DGRAM 資料報套接字
  • SOCK_SEQPACKET 有序分組套接字
  • SOCK_RAW 原始套接字

protocol的值有:

  • IPPROTO_CP TCP傳輸協議
  • IPPROTO_UDP UDP傳輸協議

返回的socket描述字,標識套接字。返回值存在於協議族(address family,AF_XXX)空間中,但沒有一個具體的地址。如果想要給它賦值一個地址,就必須呼叫bind()函式,否則就當呼叫connect()、listen()時系統會自動隨機分配一個埠。所以需要由bind來確定該套接字所對應的網路地址和埠,用來和伺服器通訊。

BIND函式 

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); 成功返回零,失敗返回-1;

bind即把協議地址和埠號賦予一個套接字,往往服務端需要bind,bind後,即可在bind的埠上監聽服務請求,客戶端往往不需要bind。

為什麼服務端需要Bind,客戶端可以不bind呢?

因為服務端始終是網路連線中的被動方,需要在一個眾所周知的埠上等待連線請求,而且作為服務棄埠應該是固定的,所以需要bind固定埠提供服務。而客戶端是發起方,我們並不關心是哪個埠與伺服器建立了連線,核心會自動分配一個無衝突的埠號,我們自己bind的話,還有可能埠衝突。當需要指定的埠通訊的時候,才需要bind。