最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語(yǔ)言基于Socket編寫(xiě)服務(wù)器端與客戶端通信的實(shí)例

 更新時(shí)間:2016年02月19日 15:10:45   作者:G1SLu  
這篇文章主要介紹了Go語(yǔ)言基于Socket編寫(xiě)服務(wù)器端與客戶端通信的實(shí)例,包括實(shí)現(xiàn)基于自定義通訊協(xié)議的Socket通信,需要的朋友可以參考下

在golang中,網(wǎng)絡(luò)協(xié)議已經(jīng)被封裝的非常完好了,想要寫(xiě)一個(gè)Socket的Server,我們并不用像其他語(yǔ)言那樣需要為socket、bind、listen、receive等一系列操作頭疼,只要使用Golang中自帶的net包即可很方便的完成連接等操作~
在這里,給出一個(gè)最最基礎(chǔ)的基于Socket的Server的寫(xiě)法:

復(fù)制代碼 代碼如下:

package main 
import ( 
    "fmt" 
    "net" 
    "log" 
    "os" 

 
 
func main() { 
 
//建立socket,監(jiān)聽(tīng)端口 
    netListen, err := net.Listen("tcp", "localhost:1024") 
    CheckError(err) 
    defer netListen.Close() 
 
    Log("Waiting for clients") 
    for { 
        conn, err := netListen.Accept() 
        if err != nil { 
            continue 
        } 
 
        Log(conn.RemoteAddr().String(), " tcp connect success") 
        handleConnection(conn) 
    } 

//處理連接 
func handleConnection(conn net.Conn) { 
 
    buffer := make([]byte, 2048) 
 
    for { 
 
        n, err := conn.Read(buffer) 
 
        if err != nil { 
            Log(conn.RemoteAddr().String(), " connection error: ", err) 
            return 
        } 
 
 
        Log(conn.RemoteAddr().String(), "receive data string:\n", string(buffer[:n])) 
 
    } 
 

func Log(v ...interface{}) { 
    log.Println(v...) 

 
func CheckError(err error) { 
    if err != nil { 
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) 
        os.Exit(1) 
    } 

唔,拋除Go語(yǔ)言里面10行代碼有5行error的蛋疼之處,你可以看到,Server想要建立并接受一個(gè)Socket,其核心流程就是

復(fù)制代碼 代碼如下:

netListen, err := net.Listen("tcp", "localhost:1024") 

conn, err := netListen.Accept() 

n, err := conn.Read(buffer) 


這三步,通過(guò)Listen、Accept 和Read,我們就成功的綁定了一個(gè)端口,并能夠讀取從該端口傳來(lái)的內(nèi)容~
Server寫(xiě)好之后,接下來(lái)就是Client方面啦,我手寫(xiě)一個(gè)HelloWorld給大家:
復(fù)制代碼 代碼如下:

package main 
 
import ( 
    "fmt" 
    "net" 
    "os" 

 
func sender(conn net.Conn) { 
        words := "hello world!" 
        conn.Write([]byte(words)) 
    fmt.Println("send over") 
 

 
 
 
func main() { 
    server := "127.0.0.1:1024" 
    tcpAddr, err := net.ResolveTCPAddr("tcp4", server) 
    if err != nil { 
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) 
        os.Exit(1) 
    } 
 
    conn, err := net.DialTCP("tcp", nil, tcpAddr) 
    if err != nil { 
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) 
        os.Exit(1) 
    } 
 
 
    fmt.Println("connect success") 
    sender(conn) 
 


可以看到,Client這里的關(guān)鍵在于
復(fù)制代碼 代碼如下:

tcpAddr, err := net.ResolveTCPAddr("tcp4", server) 

conn, err := net.DialTCP("tcp", nil, tcpAddr) 


這兩步,主要是負(fù)責(zé)解析端口和連接~
寫(xiě)好Server和Client之后,讓我們運(yùn)行一下看看:~~
成功運(yùn)行,Console出現(xiàn)Server等待連接的提示:

2016219150512234.png (1016×819)

2016219150553909.png (678×561)

2016219150608465.png (961×778)

Server端成功的收到了我們的Hello-World啦,至于后面的那行紅字,則是斷開(kāi)連接的提示~

到這里,一個(gè)最基礎(chǔ)的使用Socket的Server-Client框架就出來(lái)啦~
如果想要讓Server能夠響應(yīng)來(lái)自不同Client的請(qǐng)求,我們只要在Server端的代碼的main入口中,
在 handleConnection(conn net.Conn) 這句代碼的前面加上一個(gè) go,就可以讓服務(wù)器并發(fā)處理不同的Client發(fā)來(lái)的請(qǐng)求啦

自定義通訊協(xié)議
在上面我們做出來(lái)一個(gè)最基礎(chǔ)的demo后,已經(jīng)可以初步實(shí)現(xiàn)Server和Client之間的信息交流了~ 這一章我會(huì)介紹一下怎么在Server和Client之間實(shí)現(xiàn)一個(gè)簡(jiǎn)單的通訊協(xié)議,從而增強(qiáng)整個(gè)信息交流過(guò)程的穩(wěn)定性。
        在Server和client的交互過(guò)程中,有時(shí)候很難避免出現(xiàn)網(wǎng)絡(luò)波動(dòng),而在通訊質(zhì)量較差的時(shí)候,Client有可能無(wú)法將信息流一次性完整發(fā)送,最終傳到Server上的信息很可能變?yōu)楹芏喽巍?br />         如下圖所示,本來(lái)應(yīng)該是分條傳輸?shù)膉son,結(jié)果因?yàn)橐恍┰蜻B接在了一起,這時(shí)候就會(huì)出現(xiàn)問(wèn)題啦,Server端要怎么判斷收到的消息是否完整呢?~

2016219150627822.jpg (585×484)

 唔,答案就是這篇文章的主題啦:在Server和Client交互的時(shí)候,加入一個(gè)通訊協(xié)議(protocol),讓二者的交互通過(guò)這個(gè)協(xié)議進(jìn)行封裝,從而使Server能夠判斷收到的信息是否為完整的一段。(也就是解決分包的問(wèn)題)
        因?yàn)橹饕康氖菫榱俗孲erver能判斷客戶端發(fā)來(lái)的信息是否完整,因此整個(gè)協(xié)議的核心思路并不是很復(fù)雜:
協(xié)議的核心就是設(shè)計(jì)一個(gè)頭部(headers),在Client每次發(fā)送信息的時(shí)候?qū)eader封裝進(jìn)去,再讓Server在每次收到信息的時(shí)候按照預(yù)定格式將消息進(jìn)行解析,這樣根據(jù)Client傳來(lái)的數(shù)據(jù)中是否包含headers,就可以很輕松的判斷收到的信息是否完整了~
        如果信息完整,那么就將該信息發(fā)送給下一個(gè)邏輯進(jìn)行處理,如果信息不完整(缺少headers),那么Server就會(huì)把這條信息與前一條信息合并繼續(xù)處理。

        下面是協(xié)議部分的代碼,主要分為數(shù)據(jù)的封裝(Enpack)和解析(Depack)兩個(gè)部分,其中Enpack用于Client端將傳給服務(wù)器的數(shù)據(jù)封裝,而Depack是Server用來(lái)解析數(shù)據(jù),其中Const部分用于定義Headers,HeaderLength則是Headers的長(zhǎng)度,用于后面Server端的解析。這里要說(shuō)一下ConstMLength,這里代表Client傳入信息的長(zhǎng)度,因?yàn)樵趃olang中,int轉(zhuǎn)為byte后會(huì)占4長(zhǎng)度的空間,因此設(shè)定為4。每次Client向Server發(fā)送信息的時(shí)候,除了將Headers封裝進(jìn)去意以外,還會(huì)將傳入信息的長(zhǎng)度也封裝進(jìn)去,這樣可以方便Server進(jìn)行解析和校驗(yàn)。

復(fù)制代碼 代碼如下:

//通訊協(xié)議處理 
package protocol 
 
import ( 
    "bytes" 
    "encoding/binary" 

const ( 
    ConstHeader         = "Headers" 
    ConstHeaderLength   = 7 
    ConstMLength = 4 

 
//封包 
func Enpack(message []byte) []byte { 
    return append(append([]byte(ConstHeader), IntToBytes(len(message))...), message...) 

 
//解包 
func Depack(buffer []byte, readerChannel chan []byte) []byte { 
    length := len(buffer) 
 
    var i int 
    for i = 0; i < length; i = i + 1 { 
        if length < i+ConstHeaderLength+ConstMLength { 
            break 
        } 
        if string(buffer[i:i+ConstHeaderLength]) == ConstHeader { 
            messageLength := BytesToInt(buffer[i+ConstHeaderLength : i+ConstHeaderLength+ConstMLength]) 
            if length < i+ConstHeaderLength+ConstLength+messageLength { 
                break 
            } 
            data := buffer[i+ConstHeaderLength+ConstMLength : i+ConstHeaderLength+ConstMLength+messageLength] 
            readerChannel <- data 
 
        } 
    } 
 
    if i == length { 
        return make([]byte, 0) 
    } 
    return buffer[i:] 

 
//整形轉(zhuǎn)換成字節(jié) 
func IntToBytes(n int) []byte { 
    x := int32(n) 
 
    bytesBuffer := bytes.NewBuffer([]byte{}) 
    binary.Write(bytesBuffer, binary.BigEndian, x) 
    return bytesBuffer.Bytes() 

 
//字節(jié)轉(zhuǎn)換成整形 
func BytesToInt(b []byte) int { 
    bytesBuffer := bytes.NewBuffer(b) 
 
    var x int32 
    binary.Read(bytesBuffer, binary.BigEndian, &x) 
 
    return int(x) 


        協(xié)議寫(xiě)好之后,接下來(lái)就是在Server和Client的代碼中應(yīng)用協(xié)議啦,下面是Server端的代碼,主要負(fù)責(zé)解析Client通過(guò)協(xié)議發(fā)來(lái)的信息流:
復(fù)制代碼 代碼如下:

package main   
   
import (   
    "protocol"   
    "fmt"   
    "net"   
    "os"   
)   
   
func main() {   
    netListen, err := net.Listen("tcp", "localhost:6060")   
    CheckError(err)   
   
    defer netListen.Close()   
   
    Log("Waiting for clients")   
    for {   
        conn, err := netListen.Accept()   
        if err != nil {   
            continue   
        }   
   
        //timeouSec :=10   
        //conn.   
        Log(conn.RemoteAddr().String(), " tcp connect success")   
        go handleConnection(conn)   
   
    }   
}   
   
func handleConnection(conn net.Conn) {   
   
   
    // 緩沖區(qū),存儲(chǔ)被截?cái)嗟臄?shù)據(jù)   
    tmpBuffer := make([]byte, 0)   
   
    //接收解包   
    readerChannel := make(chan []byte, 16)   
    go reader(readerChannel)   
   
    buffer := make([]byte, 1024)   
    for {   
    n, err := conn.Read(buffer)   
    if err != nil {   
    Log(conn.RemoteAddr().String(), " connection error: ", err)   
    return   
    }   
   
    tmpBuffer = protocol.Depack(append(tmpBuffer, buffer[:n]...), readerChannel)   
    }   
    defer conn.Close()   
}   
   
func reader(readerChannel chan []byte) {   
    for {   
        select {   
        case data := <-readerChannel:   
            Log(string(data))   
        }   
    }   
}   
   
func Log(v ...interface{}) {   
    fmt.Println(v...)   
}   
   
func CheckError(err error) {   
    if err != nil {   
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())   
        os.Exit(1)   
    }   
}   

        然后是Client端的代碼,這個(gè)簡(jiǎn)單多了,只要給信息封裝一下就可以了~:

復(fù)制代碼 代碼如下:

package main   
import (   
"protocol"   
"fmt"   
"net"   
"os"   
"time"   
"strconv"   
   
)   
   
func send(conn net.Conn) {   
    for i := 0; i < 100; i++ {   
        session:=GetSession()   
        words := "{\"ID\":"+ strconv.Itoa(i) +"\",\"Session\":"+session +"2015073109532345\",\"Meta\":\"golang\",\"Content\":\"message\"}"   
        conn.Write(protocol.Enpacket([]byte(words)))   
    }   
    fmt.Println("send over")   
    defer conn.Close()   
}   
   
func GetSession() string{   
    gs1:=time.Now().Unix()   
    gs2:=strconv.FormatInt(gs1,10)   
    return gs2   
}   
   
func main() {   
    server := "localhost:6060"   
    tcpAddr, err := net.ResolveTCPAddr("tcp4", server)   
    if err != nil {   
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())   
        os.Exit(1)   
    }   
   
    conn, err := net.DialTCP("tcp", nil, tcpAddr)   
    if err != nil {   
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())   
        os.Exit(1)   
    }   
   
   
    fmt.Println("connect success")   
    send(conn)   
   
   
   
}   

這樣我們就成功實(shí)現(xiàn)在Server和Client之間建立一套自定義的基礎(chǔ)通訊協(xié)議啦,讓我們運(yùn)行一下看下效果:

2016219150649137.jpg (633×520)

成功識(shí)別每一條Client發(fā)來(lái)的信息啦~~

相關(guān)文章

  • pytorch的backward()的底層實(shí)現(xiàn)邏輯詳解

    pytorch的backward()的底層實(shí)現(xiàn)邏輯詳解

    自動(dòng)微分是一種計(jì)算張量(tensors)的梯度(gradients)的技術(shù),它在深度學(xué)習(xí)中非常有用,這篇文章主要介紹了pytorch的backward()的底層實(shí)現(xiàn)邏輯,需要的朋友可以參考下
    2023-11-11
  • py2exe 編譯ico圖標(biāo)的代碼

    py2exe 編譯ico圖標(biāo)的代碼

    py2exe 編譯ico圖標(biāo)的代碼,需要的朋友可以參考下
    2013-03-03
  • Python+Pygame實(shí)戰(zhàn)之炫舞小游戲的實(shí)現(xiàn)

    Python+Pygame實(shí)戰(zhàn)之炫舞小游戲的實(shí)現(xiàn)

    提到QQ炫舞,可能很多人想到的第一個(gè)詞是“青春”?;腥婚g,這個(gè)承載了無(wú)數(shù)人回憶與時(shí)光的游戲品牌,已經(jīng)走到了第十幾個(gè)年頭。今天小編就來(lái)給大家嘗試做一款簡(jiǎn)單的簡(jiǎn)陋版的小游戲——《舞動(dòng)青春*炫舞》,感興趣的可以了解一下
    2022-12-12
  • Python數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的定義與使用方法示例

    Python數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的定義與使用方法示例

    這篇文章主要介紹了Python數(shù)據(jù)結(jié)構(gòu)之雙向鏈表的定義與使用方法,結(jié)合實(shí)例形式分析了Python雙向鏈表的概念、原理、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-01-01
  • python grpc實(shí)現(xiàn)異步調(diào)用(不用grpc異步接口)

    python grpc實(shí)現(xiàn)異步調(diào)用(不用grpc異步接口)

    grpc同步調(diào)用更簡(jiǎn)單,但是在處理復(fù)雜任務(wù)時(shí),會(huì)導(dǎo)致請(qǐng)求阻塞,影響吞吐,本文主要介紹了python grpc實(shí)現(xiàn)異步調(diào)用,不用grpc異步接口,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-04-04
  • 利用Python讀取微信朋友圈的多種方法總結(jié)

    利用Python讀取微信朋友圈的多種方法總結(jié)

    這篇文章主要給大家介紹了關(guān)于如何利用Python讀取微信朋友圈的多種方法,對(duì)于一個(gè)新手來(lái)說(shuō)如果單獨(dú)的去爬取朋友圈的話,難度會(huì)非常大,可以借鑒這篇文章的內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • python?共現(xiàn)矩陣的實(shí)現(xiàn)代碼

    python?共現(xiàn)矩陣的實(shí)現(xiàn)代碼

    這篇文章主要介紹了python?共現(xiàn)矩陣的實(shí)現(xiàn)代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 利用Python實(shí)現(xiàn)獲取照片位置信息

    利用Python實(shí)現(xiàn)獲取照片位置信息

    Python中的exifread庫(kù),不僅僅是 GPS 信息,幾乎能獲得圖片的所有信息。本文就將利用這個(gè)庫(kù)實(shí)現(xiàn)獲取照片位置信息,感興趣的可以了解一下
    2022-08-08
  • Python+PyQt5來(lái)實(shí)現(xiàn)文件高速查找

    Python+PyQt5來(lái)實(shí)現(xiàn)文件高速查找

    這篇文章主要為大家詳細(xì)介紹了如何模擬Everything,即通過(guò)python+PyQt5來(lái)實(shí)現(xiàn)可視化文件的高速查找,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-07-07
  • Python爬蟲(chóng)之Selenium多窗口切換的實(shí)現(xiàn)

    Python爬蟲(chóng)之Selenium多窗口切換的實(shí)現(xiàn)

    這篇文章主要介紹了Python爬蟲(chóng)之Selenium多窗口切換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12

最新評(píng)論

嘉鱼县| 沅江市| 温宿县| 道孚县| 东阳市| 高邑县| 常山县| 珲春市| 大石桥市| 海门市| 阜南县| 孟州市| 芜湖县| 泗水县| 尼木县| 锦州市| 偃师市| 四会市| 桑日县| 成安县| 德化县| 横峰县| 东丽区| 普兰县| 忻州市| 新昌县| 莱阳市| 托克托县| 新竹市| 太仓市| 北川| 怀来县| 长兴县| 邯郸市| 金平| 温泉县| 棋牌| 贡嘎县| 汶上县| 广水市| 金坛市|