Socket網絡編程詳解(TCP)

socket到底是個什么東西,socket是個TCP協議嗎?我們平時很多方面都會用到socket,但確定真的了解socket嗎?

一.說起Socket我們在說什么?

Wikipedia:A network socket is an endpoint of a connection in a computer network. In Internet Protocol (IP) networks, these are often called Internet sockets. It is a handle (abstract reference) that a program can pass to the networking application programming interface (API) to use the connection for receiving and sending data. Sockets are often represented internally as integers.

個人理解:socket其實就是一根通信電纜兩端的電話終端,電話接通后就相當兩個socket建立了連接,兩個電話之間可以相互通話,兩個socket之間就可以實時收發數據,socket僅僅是一個通信工具,通信工具,通信工具重要的事說三遍(OSI模型中的第四層傳輸層的API接口,這一層通常使用兩種協議TCP或UDP來傳輸)并不是一種協議。TCP、UDP、HTTP才是我們通常理解的協議。

也就是說,Socket這個工具一般使用TCP和UDP兩種協議來通信,否則光桿socket并沒有毛用。其實我們所認識到的互聯網中的各種通信:web請求、即時通訊、文件傳輸和共享等等底層都是通過Socket工具來實現的,所以說互聯網一切皆socket。搞懂了socket你就相當于打通了任督二脈。

二.Socket的8個必備函數

socket并不可怕,我們只需掌握下面幾個C語言socket函數個人覺得就夠用了。

1. ? ?int socket(int domain, int type, int protocol);

socket函數對應于普通文件的打開操作。普通文件的打開操作返回一個文件描述字,而socket()用于創建一個socket描述符(socket descriptor),它唯一標識一個socket。這個socket描述字跟文件描述字一樣,后續的操作都有用到它,把它作為參數,通過它來進行一些讀寫操作。

正如可以給fopen的傳入不同參數值,以打開不同的文件。創建socket的時候,也可以指定不同的參數創建不同的socket描述符。

socket函數的三個參數和return分別為:

domain:即協議域,又稱為協議族(family)。通常我們只需關心這兩個協議族就夠了AF_INET、AF_INET6。AF_INET表示創建IPv4的socket,那么AF_INET6就表示創建IPv6的socket。

type:指定socket類型。常用的socket類型有,通常我們只需關心SOCK_STREAM、SOCK_DGRAM這兩個類型也就夠了,SOCK_STREAM表示TCP類型的socket,SOCK_DGRAM表示UDP類型的socket。

protocol:故名思意,就是指定協議。常用的協議有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它們分別對應TCP傳輸協議、UDP傳輸協議、STCP傳輸協議、TIPC傳輸協議。通常使用中只需記住這個參數設為0就夠了。當protocol為0時,會自動選擇type類型對應的默認協議。

return:套接口描述字。如果出現錯誤,它返回-1,并設置errno為相應的值

當我們調用socket創建一個socket時,返回的socket描述字它存在于協議族(address family,AF_XXX)空間中,但沒有一個具體的地址。如果想要給它賦值一個地址,就必須調用bind()函數,否則就當調用connect()、listen()時系統會自動隨機分配一個端口。具體下文詳細說明。

2. ? int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

正如上面所說bind()函數把一個地址族中的特定地址(IP+Port)賦給socket。例如對應AF_INET、AF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket。作為服務端我們必須給它指定一個端口號,要不然客戶端就不知道該連哪個端口了。所以服務器一般初始化問socket必須調用bind()函數綁定地址然后才能listen()而客戶端一般初始化完socket并且知道服務器IP地址和端口號就直接可以調用connect()函數進行連接了,不需要綁定自己的地址,因為系統隨機給客戶端分配的地址(IP+Port)已經默默發送到服務器了。

好了說了這么多,該說說三個參數及return的含義了:

sockfd:即socket描述字,它是通過socket()函數創建了,唯一標識一個socket。bind()函數就是將給這個描述字綁定一個名字。

addr:一個const struct sockaddr *指針,指向要綁定給sockfd的協議地址。這個地址結構根據地址創建socket時的地址協議族的不同而不同,如ipv4對應的是:

struct sockaddr_in {

? ? ? ? ? ? ? sa_family_t? ? sin_family; /* address family: AF_INET */

? ? ? ? ? ? ? in_port_t? ? ? sin_port;? /* port in network byte order */

? ? ? ? ? ? ? struct in_addr sin_addr;? /* internet address */

};

addrlen:對應的是地址的長度。

return:成功返回0,失敗返回-1

3. ? int listen(int sockfd, int backlog);

作為一個服務器,在調用socket()、bind()之后就會調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。socket()函數創建的socket默認是一個主動類型的,listen函數將socket變為被動類型的,等待客戶的連接請求。

sockfd:即為要監聽的socket描述字

backlog:相應socket可以排隊的最大連接個數。

return:成功返回0,失敗返回-1

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

作為客戶端初始化完socket,不需要bind()就直接可以connect()與服務端建立連接了。因為系統會自動生成一個隨機的地址(具體應該為本機IP+隨機端口號)。

sockfd:還沒綁定客戶端具體地址的socket描述字

addr:即將要連接到服務端的地址(IP+port)

addrlen:地址長度

return:如果是阻塞連接,成功立即返回0,如果失敗,在iOS系統上超時大約一分鐘后返回-1

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

TCP服務器端依次調用socket()、bind()、listen()之后,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()之后就向TCP服務器發送了一個連接請求。TCP服務器監聽到這個請求之后,就會調用accept()函數取接收請求,并把會這樣連接就建立好了。之后就可以開始網絡I/O操作了,即類同于普通文件的讀寫I/O操作。

sockfd:已經綁定具體服務端地址的socket描述字。

sockaddr:一般為NULL,這個參數可以理解為interface指定連接必須從哪里來的,比如是localhost、wifi還是以太網卡。NULL為任意方式。

addrlen:sockaddr地址長度

return:成功返回服務器端socket描述字否則錯誤。

6.? ssize_t write(int fd, const void *buf, size_t count);

服務端與客戶端建立了通信接下來就可以實現網絡通信了,可以調用網絡I/O進行讀寫操作了,即實現了網絡中不同進程之間的通信!

fd:要寫入的的socket文件描述符

buf:將要被寫入的緩沖區數據

count:被寫入的數據長度

return:返回值大于0,表示寫了部分數據或者是全部的數據,這樣用一個while循環不斷的寫入數據,但是循環過程中的buf參數和count參數是我們自己來更新的,也就是說,網絡編程中寫函數是不負責將全部數據寫完之后再返回的,說不定中途就返回了!返回值小于0表示出錯。代碼見第三部分

7. ssize_t read(int fd, void *buf, size_t count);

如果系統事件源有了一個讀取信號事件發生,那么我們可以調用read方法讀取網絡I/O中的數據。

fd:建立連接的socket文件描述符

buf:讀取數據后放入的緩沖區

count:緩沖區大小

return:當讀取成功時,read返回實際讀取到的字節數,這樣我們可以用一個while循環不斷的讀取數據,但是循環過程中的buf參數和count參數是我們自己來更新的,也就是說,網絡編程中寫函數是不負責將全部數據讀取完之后再返回的。如果返回值是0,表示已經讀取到文件的結束了,小于0表示是讀取錯誤。代碼見第三部分

8.? int close(int fd);

在服務器與客戶端建立連接之后,會進行一些讀寫操作,完成了讀寫操作就要關閉相應的socket描述字,好比操作完打開的文件要調用fclose關閉打開的文件。

三.循環讀取和寫入代碼

循環寫入代碼

循環讀取代碼

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

推薦閱讀更多精彩內容