前言
我們深諳信息交流的價值狈谊,那網(wǎng)絡(luò)中進程之間如何通信印蔗,如我們每天打開瀏覽器瀏覽網(wǎng)頁時亡鼠,瀏覽器的進程怎么與web服務(wù)器通信的?當(dāng)你用QQ聊天時稳强,QQ進程怎么與服務(wù)器或你好友所在的QQ進程通信场仲?這些都得靠socket?那什么是socket退疫?socket的類型有哪些渠缕?還有socket的基本函數(shù),這些都是本文想介紹的褒繁。
1 網(wǎng)絡(luò)中進程之間如何通信
本地的進程間通信(IPC)有很多種方式亦鳞,但可以總結(jié)為下面4類:
1)消息傳遞(管道、FIFO、消息隊列)
2)同步(互斥量燕差、條件變量遭笋、讀寫鎖、文件和寫記錄鎖徒探、信號量)
3)共享內(nèi)存(匿名的和具名的)
4)遠(yuǎn)程過程調(diào)用(Solaris門和Sun RPC)
但這些都不是本文的主題坐梯!我們要討論的是網(wǎng)絡(luò)中進程之間如何通信?首要解決的問題是如何唯一標(biāo)識一個進程刹帕,否則通信無從談起吵血!在本地可以通過進程PID來唯一標(biāo)識一個進程,但是在網(wǎng)絡(luò)中這是行不通的偷溺。其實TCP/IP協(xié)議族已經(jīng)幫我們解決了這個問題蹋辅,網(wǎng)絡(luò)層的“ip地址”可以唯一標(biāo)識網(wǎng)絡(luò)中的主機,而傳輸層的“協(xié)議+端口”可以唯一標(biāo)識主機中的應(yīng)用程序(進程)挫掏。這樣利用三元組(ip地址侦另,協(xié)議,端口)就可以標(biāo)識網(wǎng)絡(luò)的進程了尉共,網(wǎng)絡(luò)中的進程通信就可以利用這個標(biāo)志與其它進程進行交互褒傅。
使用TCP/IP協(xié)議的應(yīng)用程序通常采用應(yīng)用編程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已經(jīng)被淘汰),來實現(xiàn)網(wǎng)絡(luò)進程之間的通信袄友。就目前而言殿托,幾乎所有的應(yīng)用程序都是采用socket,而現(xiàn)在又是網(wǎng)絡(luò)時代剧蚣,網(wǎng)絡(luò)中進程通信是無處不在支竹,這就是我為什么說“一切皆socket”。
2 什么是Socket
上面我們已經(jīng)知道網(wǎng)絡(luò)中的進程是通過socket來通信的鸠按,那什么是socket呢礼搁?socket起源于Unix,而Unix/Linux基本哲學(xué)之一就是“一切皆文件”目尖,都可以用“打開open –> 讀寫read /write –> 關(guān)閉close”模式來操作馒吴。我的理解就是Socket就是該模式的一個實現(xiàn),socket即是一種特殊的文件瑟曲,一些socket函數(shù)就是對其進行的操作(讀/寫IO饮戳、打開、關(guān)閉)测蹲,這些函數(shù)我們在后面進行介紹莹捡。
socket一詞的起源
在組網(wǎng)領(lǐng)域的首次使用是在1970年2月12日發(fā)布的文獻IETF RFC33中發(fā)現(xiàn)的,撰寫者為Stephen Carr扣甲、Steve Crocker和Vint Cerf篮赢。根據(jù)美國計算機歷史博物館的記載齿椅,Croker寫道:“命名空間的元素都可稱為套接字接口。一個套接字接口構(gòu)成一個連接的一端启泣,而一個連接可完全由一對套接字接口規(guī)定涣脚。”計算機歷史博物館補充道:“這比BSD的套接字接口定義早了大約12年寥茫∏彩矗”
3 socket的基本操作
既然socket是“open—read/write—close”模式的一種實現(xiàn),那么socket就提供了這些操作對應(yīng)的函數(shù)接口纱耻。下面以TCP為例芭梯,介紹幾個基本的socket接口函數(shù)。
3.1 socket()函數(shù)
int socket(int domain, int type, int protocol);
socket函數(shù)對應(yīng)于普通文件的打開操作弄喘。普通文件的打開操作返回一個文件描述字玖喘,而socket()用于創(chuàng)建一個socket描述符(socket descriptor),它唯一標(biāo)識一個socket蘑志。這個socket描述字跟文件描述字一樣累奈,后續(xù)的操作都有用到它,把它作為參數(shù)急但,通過它來進行一些讀寫操作澎媒。
正如可以給fopen的傳入不同參數(shù)值,以打開不同的文件波桩。創(chuàng)建socket的時候戒努,也可以指定不同的參數(shù)創(chuàng)建不同的socket描述符,socket函數(shù)的三個參數(shù)分別為:
1)domain:即協(xié)議域突委,又稱為協(xié)議族(family)柏卤。常用的協(xié)議族有冬三,AF_INET匀油、AF_INET6、AF_LOCAL(或稱AF_UNIX勾笆,Unix域socket)敌蚜、AF_ROUTE等等。協(xié)議族決定了socket的地址類型窝爪,在通信中必須采用對應(yīng)的地址弛车,如AF_INET決定了要用ipv4地址(32位的)與端口號(16位的)的組合、AF_UNIX決定了要用一個絕對路徑名作為地址蒲每。
2)type:指定socket類型纷跛。常用的socket類型有,SOCK_STREAM邀杏、SOCK_DGRAM贫奠、SOCK_RAW唬血、SOCK_PACKET、SOCK_SEQPACKET等等(socket的類型有哪些唤崭?)拷恨。
3)protocol:顧名思義,就是指定協(xié)議谢肾。常用的協(xié)議有腕侄,IPPROTO_TCP、IPPTOTO_UDP芦疏、IPPROTO_SCTP冕杠、IPPROTO_TIPC等,它們分別對應(yīng)TCP傳輸協(xié)議酸茴、UDP傳輸協(xié)議拌汇、STCP傳輸協(xié)議、TIPC傳輸協(xié)議弊决。
注意:并不是上面的type和protocol可以隨意組合的噪舀,如SOCK_STREAM不可以跟IPPROTO_UDP組合。當(dāng)protocol為0時飘诗,會自動選擇type類型對應(yīng)的默認(rèn)協(xié)議与倡。
當(dāng)我們調(diào)用socket創(chuàng)建一個socket時,返回的socket描述字它存在于協(xié)議族(address family昆稿,AF_XXX)空間中纺座,但沒有一個具體的地址。如果想要給它賦值一個地址溉潭,就必須調(diào)用bind()函數(shù)净响,否則就當(dāng)調(diào)用connect()、listen()時系統(tǒng)會自動隨機分配一個端口喳瓣。
3.2 bind()函數(shù)
正如上面所說bind()函數(shù)把一個地址族中的特定地址賦給socket馋贤。例如對應(yīng)AF_INET、AF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket畏陕。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函數(shù)的三個參數(shù)分別為:
1)sockfd:即socket描述字配乓,它是通過socket()函數(shù)創(chuàng)建,唯一標(biāo)識一個socket惠毁。bind()函數(shù)就是給這個描述字綁定一個名字犹芹。
2)addr:一個const struct sockaddr *指針,指向要綁定給sockfd的協(xié)議地址鞠绰。這個地址結(jié)構(gòu)根據(jù)地址創(chuàng)建socket時的地址協(xié)議族的不同而不同腰埂,例如,
ipv4對應(yīng)的是:
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 */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};