1、爲何要學習socket?web
咱們打開瀏覽器瀏覽網頁時,瀏覽器的進程怎麼與web服務器通訊的?咱們用QQ聊天時,QQ進程怎麼與服務器或你好友所在的QQ進程通訊?這些都得靠socket。本地的進程間通訊(IPC)有不少種方式,但能夠總結爲下面4類:編程
但這些都不是本文的主題!咱們要討論的是網絡中進程之間如何通訊?首要解決的問題是如何惟一標識一個進程,不然通訊無從談起!在本地能夠經過進程PID來惟一標識一個進程,可是在網絡中這是行不通的。其實TCP/IP協議族已經幫咱們解決了這個問題,網絡層的「ip地址」能夠惟一標識網絡中的主機,而傳輸層的「協議+端口」能夠惟一標識主機中的應用程序(進程)。這樣利用三元組(ip地址,協議,端口)就能夠標識網絡的進程了,網絡中的進程通訊就能夠利用這個標誌與其它進程進行交互。瀏覽器
使用TCP/IP協議的應用程序一般採用應用編程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已經被淘汰),來實現網絡進程之間的通訊。就目前而言,幾乎全部的應用程序都是採用socket,而如今又是網絡時代,網絡中進程通訊是無處不在,這就是我爲何說「一切皆socket」服務器
2、什麼事Socket? 網絡
想理解socket首先得熟悉一下TCP/IP協議族, TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,定義了主機如何連入因特網及數據如何再它們之間傳輸的標準。從字面意思來看TCP/IP是TCP和IP協議的合稱,但實際上TCP/IP協議是指因特網整個TCP/IP協議族。不一樣於ISO模型的七個分層,TCP/IP協議參考模型把全部的TCP/IP系列協議歸類到四個抽象層中dom
應用層:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等socket
傳輸層:TCP,UDP函數
網絡層:IP,ICMP,OSPF,EIGRP,IGMP學習
數據鏈路層:SLIP,CSLIP,PPP,MTUspa
每一抽象層創建在低一層提供的服務上,而且爲高一層提供服務,看起來大概是這樣子的
socket
咱們知道兩個進程若是須要進行通信最基本的一個前提能可以惟一的標示一個進程,在本地進程通信中咱們可使用PID來惟一標示一個進程,但PID只在本地惟一,網絡中的兩個進程PID衝突概率很大,這時候咱們須要另闢它徑了,咱們知道IP層的ip地址能夠惟一標示主機,而TCP層協議和端口號能夠惟一標示主機的一個進程,這樣咱們能夠利用ip地址+協議+端口號惟一標示網絡中的一個進程。
可以惟一標示網絡中的進程後,它們就能夠利用socket進行通訊了,什麼是socket呢?咱們常常把socket翻譯爲套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操做抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通訊。
socket起源於UNIX,在Unix一切皆文件哲學的思想下,socket是一種"打開—讀/寫—關閉"模式的實現,服務器和客戶端各自維護一個"文件",在創建鏈接打開後,能夠向本身文件寫入內容供對方讀取或者讀取對方內容,通信結束時關閉文件。
3、socket通訊流程
socket是"打開—讀/寫—關閉"模式的實現,以使用TCP協議通信的socket爲例,其交互流程大概是這樣子的
服務器根據地址類型(ipv4,ipv6)、socket類型、協議建立socket
服務器爲socket綁定ip地址和端口號
服務器socket監聽端口號請求,隨時準備接收客戶端發來的鏈接,這時候服務器的socket並無被打開
客戶端建立socket
客戶端打開socket,根據服務器ip地址和端口號試圖鏈接服務器socket
服務器socket接收到客戶端socket請求,被動打開,開始接收客戶端請求,直到客戶端返回鏈接信息。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回鏈接信息後才返回,開始接收下一個客戶端諒解請求
客戶端鏈接成功,向服務器發送鏈接狀態信息
服務器accept方法返回,鏈接成功
客戶端向socket寫入信息
服務器讀取信息
客戶端關閉
服務器端關閉
4、三次握手
在TCP/IP協議中,TCP協議經過三次握手創建一個可靠的鏈接
第一次握手:客戶端嘗試鏈接服務器,向服務器發送syn包(同步序列編號Synchronize Sequence Numbers),syn=j,客戶端進入SYN_SEND狀態等待服務器確認
第二次握手:服務器接收客戶端syn包並確認(ack=j+1),同時向客戶端發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態
第三次握手:第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手
5、socket的基本操做
既然socket是「open—write/read—close」模式的一種實現,那麼socket就提供了這些操做對應的函數接口。下面以TCP爲例,介紹幾個基本的socket接口函數。
(1)socket()函數
socket函數對應於普通文件的打開操做。普通文件的打開操做返回一個文件描述字,而socket()用於建立一個socket描述符(socket descriptor),它惟一標識一個socket。這個socket描述字跟文件描述字同樣,後續的操做都有用到它,把它做爲參數,經過它來進行一些讀寫操做。正如能夠給fopen的傳入不一樣參數值,以打開不一樣的文件。建立socket的時候,也能夠指定不一樣的參數建立不一樣的socket描述符,socket函數的三個參數分別爲:
注意:並非上面的type和protocol能夠隨意組合的,如SOCK_STREAM不能夠跟IPPROTO_UDP組合。當protocol爲0時,會自動選擇type類型對應的默認協議。
當咱們調用socket建立一個socket時,返回的socket描述字它存在於協議族(address family,AF_XXX)空間中,但沒有一個具體的地址。若是想要給它賦值一個地址,就必須調用bind()函數,不然就當調用connect()、listen()時系統會自動隨機分配一個端口。
(2)bind()函數
正如上面所說bind()函數把一個地址族中的特定地址賦給socket。例如對應AF_INET、AF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket。
(3)listen()、connect()函數
若是做爲一個服務器,在調用socket()、bind()以後就會調用listen()來監聽這個socket,若是客戶端這時調用connect()發出鏈接請求,服務器端就會接收到這個請求。
(4)accept()函數
TCP服務器端依次調用socket()、bind()、listen()以後,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()以後就想TCP服務器發送了一個鏈接請求。TCP服務器監聽到這個請求以後,就會調用accept()函數取接收請求,這樣鏈接就創建好了。以後就能夠開始網絡I/O操做了,即類同於普通文件的讀寫I/O操做
(5)read()、write()等函數
萬事具有隻欠東風,至此服務器與客戶已經創建好鏈接了。能夠調用網絡I/O進行讀寫操做了,即實現了網咯中不一樣進程之間的通訊!網絡I/O操做有下面幾組:
我推薦使用recvmsg()/sendmsg()函數,這兩個函數是最通用的I/O函數
(6)close()函數
在服務器與客戶端創建鏈接以後,會進行一些讀寫操做,完成了讀寫操做就要關閉相應的socket描述字,比如操做完打開的文件要調用fclose關閉打開的文件。
6、socket實例
代碼下載http://files.cnblogs.com/files/qtiger/SocketExmple.zip