簡單理解Socket

題外話

前幾天和朋友聊天,朋友問我怎麼最近不寫博客了,一個是由於最近在忙着公司使用的一些控件的開發,瀏覽器兼容性搞死人;但主要是由於這段時間一直在看html5的東西,看到web socket時以爲頗有意思,動手寫幾個demo,但web socket須要特定的服務器支持,因爲標準制定工做還沒完成,因此沒有多少主流的服務器支持,本身在網上下載了幾個實現,包括php的、C#的、甚至Node.js的,但一個是協議變化比較大,不少代碼已通過時了,再就是有一些支持最新的標準,可是我想稍微改造一下,看人家源代碼的時候雲裏霧裏,看看別人的代碼行數也很少,決定本身實現一個。php

悲劇由此開始,雖然哥們兒國內非知名工科大學畢業,但好歹也是科班CS出身,但大學得過且過,什麼TCP/IP協議,什麼socket了都沒概念。爲了作出一個簡單的支持廣播的websocket server,在網上找了不少相關代碼,左抄一句,右抄一句,弄了一個星期居然仍是漏洞百出,調試不起來,只好從頭來過了,先補一些基本知識,而後再一步步根據原理實現,今天終於實現了絕大部分功能,由此真的感覺到了,搞計算機必須得有理論指導實踐,不然只能像個沒頭蒼蠅處處亂撞。html

TCP/IP

要想理解socket首先得熟悉一下TCP/IP協議族, TCP/IPTransmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,定義了主機如何連入因特網及數據如何再它們之間傳輸的標準,html5

從字面意思來看TCP/IP是TCP和IP協議的合稱,但實際上TCP/IP協議是指因特網整個TCP/IP協議族。不一樣於ISO模型的七個分層,TCP/IP協議參考模型把全部的TCP/IP系列協議歸類到四個抽象層中web

應用層:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等編程

傳輸層:TCP,UDP瀏覽器

網絡層:IP,ICMP,OSPF,EIGRP,IGMP服務器

數據鏈路層:SLIP,CSLIP,PPP,MTUwebsocket

每一抽象層創建在低一層提供的服務上,而且爲高一層提供服務,看起來大概是這樣子的網絡

                       

估計有興趣打開此文的同窗都對此有必定了解了,加上我也是隻知其一;不知其二,因此就不詳細解釋,有興趣同窗能夠上網上搜一下資料dom

維基百科

百度百科

在TCP/IP協議中兩個因特網主機經過兩個路由器和對應的層鏈接。各主機上的應用經過一些數據通道相互執行讀取操做


socket

咱們知道兩個進程若是須要進行通信最基本的一個前提能可以惟一的標示一個進程,在本地進程通信中咱們可使用PID來惟一標示一個進程,但PID只在本地惟一,網絡中的兩個進程PID衝突概率很大,這時候咱們須要另闢它徑了,咱們知道IP層的ip地址能夠惟一標示主機,而TCP層協議和端口號能夠惟一標示主機的一個進程,這樣咱們能夠利用ip地址+協議+端口號惟一標示網絡中的一個進程。

可以惟一標示網絡中的進程後,它們就能夠利用socket進行通訊了,什麼是socket呢?咱們常常把socket翻譯爲套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操做抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通訊。

socket起源於UNIX,在Unix一切皆文件哲學的思想下,socket是一種"打開—讀/寫—關閉"模式的實現,服務器和客戶端各自維護一個"文件",在創建鏈接打開後,能夠向本身文件寫入內容供對方讀取或者讀取對方內容,通信結束時關閉文件。

socket通訊流程

socket是"打開—讀/寫—關閉"模式的實現,以使用TCP協議通信的socket爲例,其交互流程大概是這樣子的

服務器根據地址類型(ipv4,ipv6)、socket類型、協議建立socket

服務器爲socket綁定ip地址和端口號

服務器socket監聽端口號請求,隨時準備接收客戶端發來的鏈接,這時候服務器的socket並無被打開

客戶端建立socket

客戶端打開socket,根據服務器ip地址和端口號試圖鏈接服務器socket

服務器socket接收到客戶端socket請求,被動打開,開始接收客戶端請求,直到客戶端返回鏈接信息。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回鏈接信息後才返回,開始接收下一個客戶端諒解請求

客戶端鏈接成功,向服務器發送鏈接狀態信息

服務器accept方法返回,鏈接成功

客戶端向socket寫入信息

服務器讀取信息

客戶端關閉

服務器端關閉

三次握手

在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狀態,完成三次握手

定睛一看,服務器socket與客戶端socket創建鏈接的部分其實就是大名鼎鼎的三次握手

 socket編程API

前面提到socket是"打開—讀/寫—關閉"模式的實現,簡單瞭解一下socket提供了哪些API供應用程序使用,仍是以TCP協議爲例,看看Unix下的socket API,其它語言都很相似(PHP甚至名字都幾乎同樣),這裏我就簡單解釋一下方法做用和參數,具體使用有興趣同窗能夠看看博客參考中的連接或者上網搜索

int socket(int domain, int type, int protocol);

根據指定的地址族、數據類型和協議來分配一個socket的描述字及其所用的資源。

domain:協議族,經常使用的有AF_INETAF_INET6AF_LOCALAF_ROUTE其中AF_INET表明使用ipv4地址

type:socket類型,經常使用的socket類型有,SOCK_STREAMSOCK_DGRAMSOCK_RAWSOCK_PACKETSOCK_SEQPACKET

protocol:協議。經常使用的協議有,IPPROTO_TCPIPPTOTO_UDPIPPROTO_SCTPIPPROTO_TIPC

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

把一個地址族中的特定地址賦給socket

sockfd:socket描述字,也就是socket引用

addr:要綁定給sockfd的協議地址

addrlen:地址的長度

一般服務器在啓動的時候都會綁定一個衆所周知的地址(如ip地址+端口號),用於提供服務,客戶就能夠經過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。這就是爲何一般服務器端在listen以前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。

int listen(int sockfd, int backlog);

監聽socket

sockfd:要監聽的socket描述字

backlog:相應socket能夠排隊的最大鏈接個數 

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

鏈接某個socket

sockfd:客戶端的socket描述字

addr:服務器的socket地址

addrlen:socket地址的長度

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

 TCP服務器監聽到客戶端請求以後,調用accept()函數取接收請求

sockfd:服務器的socket描述字

addr:客戶端的socket地址

addrlen:socket地址的長度

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

讀取socket內容

fd:socket描述字

buf:緩衝區

count:緩衝區長度

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

向socket寫入內容,其實就是發送內容

fd:socket描述字

buf:緩衝區

count:緩衝區長度

int close(int fd);

socket標記爲以關閉使相應socket描述字的引用計數-1,當引用計數爲0的時候,觸發TCP客戶端向服務器發送終止鏈接請求。

參考

Linux Socket編程(不限Linux)

揭開Socket編程的面紗

PS. 有同窗看完後發現沒有demo示例,參考中的示例已經很不錯了,我就不班門弄斧了,並且我用C#實現了一個websocket server,接下來的博客中會有介紹。另外因爲剛剛實際接觸socket,文中謬誤較多,還望你們批評指正,文章內容主要參考上面兩個博文,圖片所有來源於網絡,在百度圖片搜索得來,沒法註明第一源地址,若有版權問題請站內信聯繫,第一時間處理。

相關文章
相關標籤/搜索