說到Socket
,想必你們會以爲陌生又熟悉。許多同窗據說過Socket,但僅僅知道它翻譯成中文叫作套接字
,除此以外彷佛並無太多的瞭解了。那麼今天我就來拋磚引玉地聊一聊Socket。有人說web
The lower application layers are all about socket programming
應用的底層全是和socket打交道編程
一看到涉及底層,有的同窗就表示:
其實這些東西並不深奧,只要花一些時間去看,確定是可以看懂的,而且一但找到了點兒感受,會以爲Socket很是有趣。
你難道很差奇瀏覽器是怎樣和web服務器勾搭在一塊兒來取悅你的嗎?許多網絡應用都經過Socket來交流,因此Socket在網絡編程裏佔有了很重要的地位。
那麼言歸正傳,到底什麼是Socket
的呢?——大學教材上的答案是套接字
。我我的以爲這是一個不太好的翻譯,雖說仔細一想有那麼點兒意思,可是99%的人即便看見套接字
這個詞,依然不知道是什麼鬼,因此沒有翻譯的必要。就像Rap
你硬要說中文翻譯叫拉普
也沒啥意義對吧。
在Unix中,有一種說法叫瀏覽器
Everything is a file
一切皆文件服務器
因此你只須要記住Socket是某種類型文件的抽象
怎麼理解這句話呢?想象一下,假設你要開發一個網絡應用,須要在兩個客戶端之間發消息。整個過程可能包含如下步驟:網絡
文件
。它是一個鏈接了兩個用戶的文件,任何一個用戶向Socket裏寫數據,另外一個用戶都能看獲得,無論這兩個用戶分佈在世界上相距多麼遙遠的角落,感受就像坐在一塊兒傳紙條同樣。Socket
應該更容易理解吧?這種抽象是很是重要的,由於它屏蔽了更底層的東西,我就想寫個程序發送下數據,爲何要關係物理層怎麼傳輸呢,對吧。這樣的話,網絡編程是否是就很是簡單了呢?
那麼下面咱們用Go語言做爲示例,演示一下。併發
package main import ( "fmt" "net" "os" ) func main() { //使用tcp協議,要監聽的是6666端口 tcpAddr, err := net.ResolveTcpAddr("tcp", "localhost:6666") checkError(err) //開始監聽 fd, err := net.ListenTcp("tcp", tcpAddr) checkError(err) for { //獲取Socket conn, err := fd.Accept() if err != nil { continue } //你的邏輯 go Handle(conn) } } func Handle(conn *net.Conn) { //You can do anything you want to here... } func checkError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
端口是什麼概念?能夠近似這麼想:一臺電腦就是你家的小區,你買東西若是填的地址是你家小區,那麼快遞員最多能把東西送到小區門口;可是若是你寫上了你家的門牌號,那麼快遞員就能送到你家門口。一樣的,電腦上同時運行着不少程序,好比QQ,旺旺…可是電腦只有只有一個IP地址,一條消息來了沒人知道這個消息是給誰的,因而就有了端口的概念。QQ在這臺電腦的4567端口,旺旺在這臺電腦1234端口。發消息的人只要知道它在什麼端口,就能準確地把消息發過來了。
一樣的,網絡通訊兩端的人得事先約定好一個端口,而後一我的守着這個端口,待另外一方鏈接了這個端口,這纔算創建了Socket鏈接。就好兩我的打電話,不須要關心信號怎麼轉換和傳輸,但在創建此次通話以前必須有人撥號,同時有人守在電話旁。
因而上面的代碼應該就能夠理解了吧?
一個程序猿走到"localhost:6666"這個「電話」旁邊app
tcpAddr,err := net.ResolveTcpAddr("tcp","localhost:6666")`
而後坐下來等電話響socket
fd,err := net.ListenTcp("tcp", tcpAddr)
他也不知道女友何時打電話過來,因而開始了漫長的等待tcp
//一個死循環 for { conn,err := fd.Accept() //電話沒有響就一直堵在上面這條語句 }
在漫長地等待中,忽然電話響了,而後開始了一段佳話(程序終於不堵了,接着向下執行)函數
go Handle(conn)
Handle
方法就用來處理對話,數據都在conn
裏面,只須要學習相關的API就能知道怎麼把具體的內容取出來了。
整個過程是否是很簡單?
有些機智的同窗可能已經發現了,要這樣的話,兩我的都在等對方打電話過來,豈不是就終身無緣了(這種誤會就像韓劇)。對的,因此咱們還須要知道,如何給對方撥號。這是很關鍵的一步,通常妹子很差意思,那麼天然咱們得上了。
怎麼撥號呢?請看代碼:
import ( "fmt" "io/ioutil" "net" "os" ) func main() { tcpAddr, err := net.ResolveTcpAddr("tcp", "localhost:6666") checkError(err) conn, err := net.DialTcp("tcp", nil, tcpAddr) checkError(err) _, err = conn.Write([]byte("妹子,約嗎?")) result, err := ioutil.ReadAll(conn) //不約,叔叔咱們不約 } func checkError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
解釋一下,通常妹子都比較含蓄,告訴你聯繫方式不那麼直接,你得破解一下
tcpAddr := net.ResolveTcpAddr("tcp", "localhost:6666")
呵呵,嘴上說不要身體卻很誠實嘛,這麼容易就破解了。(實際上是Go的包比較好用好嗎!)
而後按着電話號撥打電話
conn, err := net.DialTcp("tcp", nil, tcpAddr)
電話打通了,conn
就表明此次通話。屌絲們已經急不可耐了,因而大喊一句:
_, err = conn.Write([]byte("妹子,約嗎?"))
爲何我第一個返回值用_
,這表示我不想知道函數的返回結果,即Write了多少個字節。我問妹子約不約,你說我關不關心這句話包含幾個字節?
result, err := ioutil.ReadAll(conn)
妹子給的回覆就在result裏,慢慢去琢磨吧……
以上示例並不完整,完整的示例網上處處可見,但願你們能本身寫一寫。
本篇只是粗淺地講了講什麼是Socket編程以及基本過程,以後會有更細緻地講解(好比:併發)。
做者: PHPBird 連接:http://www.imooc.com/article/1668來源:慕課網