淺談Socket編程

淺談Socket編程

說到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是某種類型文件的抽象圖片描述
怎麼理解這句話呢?想象一下,假設你要開發一個網絡應用,須要在兩個客戶端之間發消息。整個過程可能包含如下步驟:網絡

  • 客戶端組裝數據
  • 客戶端之間約定好數據格式
  • 客戶端向指定地址發送請求
  • DNS服務器解析請求地址
  • DNS沒有找到地址,而後跳轉到另外一個DNS,一直到找到爲止
  • 返回客戶端真實的IP
  • 客戶端向對應IP創建鏈接請求(三次握手)
  • 開始發送數據,窗口以2的k次冪大小滑動
    ……
    以上省略一本頁數爲1047的《TCP/IP》圖片描述
    有人可能已經噴了,你不是說Socket編程很簡單嗎,這還叫簡單?
    正由於這很複雜,因此前人們對這個過程進行了一種抽象,來幫助咱們編程。
    你不就是想把數據發給對方嗎?組裝數據而後發給某人這個過程,和組裝好數據而後寫到某個文件裏有什麼區別呢?對了,沒有區別。
    Socket就是一種特殊的文件。它是一個鏈接了兩個用戶的文件,任何一個用戶向Socket裏寫數據,另外一個用戶都能看獲得,無論這兩個用戶分佈在世界上相距多麼遙遠的角落,感受就像坐在一塊兒傳紙條同樣。
    這麼講Socket應該更容易理解吧?這種抽象是很是重要的,由於它屏蔽了更底層的東西,我就想寫個程序發送下數據,爲何要關係物理層怎麼傳輸呢,對吧。
    因此有了Socket的概念以後,咱們在兩個客戶端之間發送消息可能就是這樣的:
  • 指定對方的地址
  • 打開一個和對方鏈接的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來源:慕課網

相關文章
相關標籤/搜索