Linux C Socket Api詳解

點擊上方藍字可直接關注!方便下次閱讀。如果對你有幫助,麻煩點個在看或點個贊,感謝~

文章主要梳理於《UNIX 環境高級編程第二版》 第十六章 網絡IPC: 套接字

 

以前都只是在網上搜的能用的例子,對一些參數不是很清楚,這次彙總。而且網絡通信還是很常用的通信手段。

 

UNIX 環境高級編程對Socket通信的描述是套接字網絡IPC(進程間通信),可以用於計算機間通信也可用於計算機內通信,管道、消息隊列、信號量以及共享內存等都是屬於計算機內通信的情況。

 

一、 套接字Api詳細介紹

1. 套接字描述符

首先會先到的是文件描述符,對Linux一切皆文件的哲學又多懂了一點兒點兒。

套接字是通信端點的抽象。與應用程序使用文件描述符一樣,訪問套接字需要使用套接字描述符。套接字描述符在UNIX系統是用文件描述符實現的。

#include  <sys/socket.h>
int  socket (int domain, int type, int protocal);
返回值:成功返回文件(套接字)描述符,出錯返回-1

參數domain()確定通信的特性,包括地址格式。各個域都有自己的格式表示地址,表示各個域的常數都以AF_開頭,意指地址族(address family).

參數type確定套接字的類型,進一步確定通信特徵。下圖給出了一些類型,但在實現中可以自由增加對其他類型的支持。

參數protocol通常是0,表示按給定的域和套接字類型選擇默認的協議。當對同一域和套接字類型支持多個協議時,可以使用proticol參數選擇一個特定協議。在A_FINET通信域中套接字類型SOCK_STREAM的默認協議是TCP(傳輸控制協議)A_FINET通信域中套接字類型SOCK_DGRAM的默認協議是UDP(用戶數據報協議)

    字節流(SOCK_STREAM)要求在交換數據之前,在本地套接字和遠程套接字之間建

立一個邏輯聯繫。

 

Tcp沒有報文界限,提供的是字節流服務。之前寫過Qt傳輸圖片的拆包與解包,原因就是如此吧。

 

調用socket與調用open類型,均可獲得用於輸入、輸出的文件描述符。不用的時候記得close關閉。

2. 尋址

如何確定一個目標通信進程?

進程的標識有兩個部分:計算機的網絡地址可以確定網絡上與之想要通信的計算機

服務可以確定計算機上的特定進程。

2.1 字節序

在同一臺計算機上進程間通信時,一般無需考慮字節序。

TCP/IP協議棧使用大端字節序。有關字節序大家可自行百度。

Linux系統是小端字節序。

2.2 地址格式

地址確定了特定通信域中的套接字端點,地址格式與特定的通信域相關。爲使不同格式的地址能夠被傳入到套接字函數,地址被強轉換成通用的地址結構sockaddr表示。

Linux中,sockaddr_in定義如下:

struct sockaddr_in {
sa_family_t    sin_family;
in_port_t      sin_port;
struct  in_addr   sin_addr;
unsigned char    sin_zero[8];
};

其中成員sin_zero爲填充字段,必須全部置0. 所以在網上搜到的例子有使用bzero.

我目前使用的ubuntu定義如下:

/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;      /* Port number.  */
    struct in_addr sin_addr;    /* Internet address.  */


    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
         __SOCKADDR_COMMON_SIZE -
         sizeof (in_port_t) -
         sizeof (struct in_addr)];
  };

還有很多關於地址查詢的函數,這裏就不一一列舉了。

3. 將套接字與地址綁定

使用bind函數將地址綁定到一個套接字上。

#include  <sys/socket.h>
int bind(int  sockfd, const struct sockaddr * addr, socklen_t  len);
返回值:成功返回0,出錯返回-1

參數socklen_t使用sizeof來計算就好了。

 

對於使用地址的一些限制:

端口號不能小於1024,除非該進程具有相應的特權(即爲超級用戶)。可見規則總是因人而異,計算機也是如此~

對於因特網域,如果指定IP地址爲ADDR_ANY,套接字端點可以被綁定到所有的系統網絡接口。

注意:linux的man命令可以查看api的詳細說明,而且還有例子,也挺不錯的。

4. 建立連接

1> connect

如果處理的是面向連接的網絡服務(SOCK_STREAM或SOCK_SEQPACKET),在開始交換數據前,需要在請求服務的進程套接字(客戶端)和提供服務的進程套接字(服務器)之間建立一個連接。使用connect.

#include  <sys/socket.h>
int connect(int  sockfd, const struct sockaddr  *addr,  socklen_t  len);
返回值:成功返回0,出錯返回-1

誒,這個參數好熟悉呀,和bind函數的參數一模一樣呀~

client連接server時,由於一些原因,連接可能會失敗。可以使用指數補償的算法解決,瞭解一下即可。

 

2> listen

server調用listen來宣告可以接受連接請求:

#include  <sys/socket.h>
Int listen(int  sockfd, int  backlog);
返回值:成功返回0,出錯返回-1

參數backlog提供了一個提示,用於表示該進程所要入隊的連接請求數量。其值由系統決定,但上限由<sys/socket.h>SOMAXCONN指定。

一旦隊列滿,系統會拒絕多餘的連接請求。

3> accept

一旦服務器調用了listen,套接字就能接收連接請求。使用函數accept獲得連接請求並建立連接。

#include  <sys/socket.h>
Int accept(int sockfd,  struct sockaddr *restrict  addr, socklen_t *restrict  len);
返回值:成功返回文件(套接字)描述符,出錯返回-1

函數accept所返回的文件描述符是套接字描述符,該描述符連接到調用connect的客戶端。這個新的套接字描述符和原始套接字(sockfd)具有相同的套接字類型和地址族。傳給accept的原始套接字沒有關聯到這個連接,而是繼續保持可用狀態並接受其他連接請求。

如果不關心客戶端標識,可以將addr和len設置爲NULL,否則addr存放的是連接的客戶端的地址。

如果沒有連接請求等待處理,accept會阻塞直到有請求到來。另外server可以使用pollselect來等待一個請求的到來。

 

5. 數據傳輸

既然將套接字端點表示爲文件描述符,那麼只要建立連接,就可以使用read和write來通過套接字通信。readwrite函數我幾乎不用,瞭解一下即可。

1> send

#include  <sys/socket.h>
Int send(int sockfd,  const void *buf,  size_t  nbytes,  int  flags);
返回值:成功返回發送的字節數,出錯返回-1

注意:如果send成功返回,並不一定並表示連接的另一端的進程接收數據。可以保證的是數據已經無誤的發送到網絡上。

 

標誌我一直用的是0

2> recv

#include  <sys/socket.h>
int recv(int sockfd,  const void *buf,  size_t  nbytes,  int  flags);
返回值:以字節計數的消息長度,若無可用消息或對方已經按序結束則返回0,      出錯返回-1

仍然一直是0

如果想定位發送者,可以使用recvfrom來得到數據發送者的源地址。

3> recvfrom

#include  <sys/socket.h>
int recv(int sockfd,  void *restrict buf, size_t len, int flag, 
struct sockaddr *restrict  addr, 
socklen_t *restrict  len);
返回值:以字節計數的消息長度,若無可用消息或對方已經按序結束則返回0,      出錯返回-1

因爲可以獲得發送者的地址,recvfrom通常用於無連接套接字。否則,recvfrom等同於recv

 

 

二、 小結

這裏面再提一個帶外數據,感興趣的同志可以自行百度。

之前寫過一個server和client的例子,連接如下,可對應本文做對比閱讀。

Linux Socket Server 與 Client 例子

 

個人覺得這只是套接字的入門,如果一個服務器要連接多個客戶端呢?以後有機會和大家一起分享下select的套接字用法。

 

思考:畢業後的學習與在學校的學習有什麼區別呢 ?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章