socket 編程(一)——基本概念

在類 Unix 的操作系統中,I/O 操作都是通過讀寫文件描述符(file descriptor)來進行的。文件描述符是類 Unix 系統中的一個重要概念。socket 操作的是網絡 I/O,所以也沿用這種設計思路。socket 有對應的套接字描述符 sockfd。很多用于操作文件描述符 fd 的操作都可以用于操作 sockfd。就如常用的文件操作 write()/read() 就可以用于 sockfd,來實現數據的發送和接收。

在接著介紹 socket 之前,先跳出來了解一下文件描述符

文件描述符(File descriptor,后文用 fd 替代),是一個非負整數。當進程打開或者創建一個文件時,系統內核就會向進程返回一個 fd 用來指代這個文件。對文件讀寫時,就將 fd 作為參數傳遞給 write() / read() 函數。

在 Unix shell 中,文件描述符0與進程標準輸入相關聯,文件描述符1與標準輸出相關聯,文件描述符2與標準錯誤相關聯。

下面這張圖描述了,進程、文件描述符以及文件間的關系:

file-descriptor.png

繼續介紹 socket

socket 地址

/*==================================
 * socket 的地址結構,作為 bind(), 
 * connect(),accept() 的參數
 *================================*/
struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

/*==================================
 * sockaddr_in 是 IPv4 因特網地址,
 * 是具體的 sockaddr。使用時需要強制
 * 轉換成 sockaddr
 * sin_family = AF_INET
 *================================*/
struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

/*==================================
 * 4字節的 IPv4 地址
 *================================*/
struct in_addr {
   unsigned long s_addr;
};

這些地址是用于 socket 函數的。sockaddr_in 是專用于 IPv4 通信的。IPv6 的地址結構,這里未列出

socket 的地址分為 4類:

  • AF_INET, IPv4 因特網域, 用于不同 pc 通過網絡來通信
  • AF_INET6, IPv6 因特網域
  • AF_UNIX, (AF_LOCAL)UNIX 域, 用于同一臺 pc 下不同進程間通信
  • AF_UNSPEC, 未指定

可以看出 socket 不僅可以使用 ip 地址作為 sockaddr,還能使用其他協議族的地址。這正和 socket 的設計目標一致:同樣的接口既可以用于計算機間通信還能用于計算機內通信

socket 函數

#include <sys/socket.h>

//建立連接
int socket(int domain, int type, int protocal);
int bind(int sockfd, const struct sockaddr *addr, socklent_t len);
int connect(int sockfd, const struct sockaddr *addr, socklen_t lent);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

//數據傳輸
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct      sockaddr *destaddr, socklen_t destlen);
ssize_t sendmsg(int socklen, const struct msghdr *msg, int flags);
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct      sockaddr *restrict addr, socklen_t *restrict addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

//關閉連接
#include <unistd.h>
int close(int sockfd);
#include <sys/socket.h>
int shutdown(int sockfd, int how);

note:

1、strict 關鍵字, 用于告知編譯器, strict 修飾的指針所指向的內容,只能通過這個指針修改。沒有其他修改途徑,有利于后面的代碼優化

上面將 socket 相關函數分為了三類: 建立連接、傳輸數據、關閉連接。這么分的目的是為了和 TCP 傳輸的過程相對應。盡管 socket 并不僅僅用于 tcp 傳輸,這里為了和我們熟知的網絡知識關聯起來,就以 tcp 連接為例來介紹 socket

對于一個 c/s 模型的服務來說,client 和 server 間的通信可以簡化以下兩個部分:

client

  • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
  • connect( sockfd, &serv_addr, sizeof(serv_addr) )
  • wirte( sockfd, buffer, strlen(buffer) );

客戶端創建一個 socket ,返回一個套接字描述符 sockfd。接著,connect() 函數,向服務器發起連接。這個步驟可以看成,client 和 server 進行 tcp 三次握手。

server

  • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
  • bind(sockfd, &serv_addr, sizeof(serv_addr) )
  • listen(sockfd, 1024);
  • newsockfd = accept(sockfd, &cli_addr, sizeof(cli_addr) );

服務端創建一個 socket,返回一個套接字描述符 sockfd。bind( ) 將服務器的地址和其中一個端口綁定到 sockfd 上。調用 listen( ) ,開始在綁定的端口上監聽來自客戶端的連接。當有新的客戶端連接到來時,調用 accept( ) 創建一個新的套接字描述符 newsockfd , 來處理這個新連接。之前的 sockfd 繼續監聽是否有新連接。

這個過程,可以與下面這張圖對應起來

socket_client_server.jpg

在前文中,介紹了最為典型的 socket 類型 Stream Sockets。stream socket 主要用于 tcp 傳輸

除此之外,socket 還有其他 3種類型

  • Data gram Sockets ,用于 udp 傳輸
  • Raw Sockets ,用來訪問底層協議,主要用來開發新的協議
  • Sequenced Packet Sockets,和 stream sockets 相似,但是它保留了邊界

參考

[1] Unix Socket Tutorial

[2] UNIX高級環境編程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函數

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • 什么是TCP/IP、UDP? TCP/IP(Transmission Control Protocol/Inter...
    liuboxx1閱讀 988評論 0 1
  • 網絡模型 物理層 物理層表示的是比特流傳輸,通常包括串口/COM口、并行/LPT口、USB、網線接口、電話線接口;...
    秋風弄影閱讀 731評論 0 2
  • 最近在學習Python看了一篇文章寫得不錯,是在腳本之家里的,原文如下,很有幫助: 一、網絡知識的一些介紹 soc...
    qtruip閱讀 2,735評論 0 6
  • 我正在參加怦然心動·邂逅你的11封情書——1111情書交友創作大賽,快來給我寫情書吧。 昵稱:卜卜君 地點:江蘇 ...
    卜卜君閱讀 446評論 7 4
  • 沿著家的小徑走,只有我一人。 我像撫摸嬰兒的面龐一樣撫摸著樹干,看著它歷經歲月洗禮的枝干,細小的疤痕。我想起城市里...
    小琦怪閱讀 328評論 0 1