Go語言之併發程式設計(三)

2022-04-05 05:52:30 字數 3768 閱讀 5869

telnet回音伺服器

telnet協議是tcp/ip協議族中的一種。它允許使用者(telnet客戶端)通過乙個協商過程與乙個遠端裝置進行通訊。本例將使用一部分telnet協議與伺服器進行通訊。

伺服器的網路庫為了完整展示自己的**實現了完整的收發過程,一般比較傾向於使用傳送任意封包返回原資料的邏輯。這個過程類似於對著大山高喊,大山把你的聲音原樣返回的過程。也就是回音(echo)。本節使用go語言中的socket、goroutine和通道編寫乙個簡單的telnet協議的回音伺服器。

回音伺服器的**分為4個部分,分別是接受連線、會話處理、telnet命令處理和程式入口。

1.接受連線

回音伺服器能同時服務於多個連線。要接受連線就需要先建立偵聽器,偵聽器需要乙個偵聽位址和協議型別。就像你想賣東西,需要先確認賣什麼東西,賣東西的型別就是協議型別,然後需要乙個店面,店面位於街區的某個位置,這就是偵聽器的位址。乙個伺服器可以開啟多個偵聽器,就像乙個街區可以有多個店面。街區上的編號對應的就是位址中的埠號,如圖1-2所示。

圖1-2   ip和埠號

埠號:16 位無符號整型值,一共有 65536 個有效埠號。

我們把每個客戶端連線處理業務的過程叫做會話。在會話中處理的操作和接受連線的業務並不衝突可以同時進行。就像銀行有 3 個視窗,喊號器會將使用者分配到不同的櫃檯。這裡的喊號器就是 accept 操作,視窗的數量就是 cpu 的處理能力。因此,使用 goroutine 可以輕鬆實現會話處理和接受連線的併發執行。

如圖1-3清晰地展現了這一過程。

圖1-3   socket處理過程

go語言中可以根據實際會話數量建立多個goroutine,並自動的排程它們的處理。

server.go

package main

import (

"fmt"

"net"

)// 服務邏輯, 傳入位址和退出的通道

func server(address string, exitchan chan int)

// 列印偵聽位址, 表示偵聽成功

fmt.println("listen: " + address)

// 延遲關閉偵聽器

defer l.close()

// 偵聽迴圈

for

// 根據連線開啟會話, 這個過程需要並行執行

go handlesession(conn, exitchan)}}

**說明如下:

2.會話處理

每個連線的會話就是乙個接收資料的迴圈。當沒有資料時,呼叫reader.readstring會發生阻塞,等待資料的到來。一旦資料到來,就可以進行各種邏輯處理。

回音伺服器的基本邏輯是「收到什麼返回什麼」,reader.readstring可以一直讀取socket連線中的資料直到碰到期望的結尾符。這種期望的結尾符也叫定界符,一般用於將tcp封包中的邏輯資料拆分開。下例中使用的定界符是回車換行符(「\r\n」),http協議也是使用同樣的定界符。使用reader.readstring()函式可以將封包簡單地拆分開。

如圖1-4所示為telnet資料處理過程。

圖1-4   telnet資料處理過程

回音伺服器需要將收到的有效資料通過socket傳送回去。

session.go

package main

import (

"bufio"

"fmt"

"net"

"strings"

)// 連線的會話邏輯

func handlesession(conn net.conn, exitchan chan int)

// echo邏輯, 發什麼資料, 原樣返回

conn.write(byte(str + "\r\n"))

} else }}

**說明如下:

3.telnet命令處理

telnet是一種協議。在作業系統中可以在命令列使用telnet命令發起tcp連線。我們一般用telnet來連線tcp伺服器,鍵盤輸入一行字元回車後,即被傳送到伺服器上。

在下例中,定義了以下兩個特殊控制指令,用以實現一些功能:

telnet命令處理:

telnet.go

package main

import (

"fmt"

"strings"

)func processtelnetcommand(str string, exitchan chan int) bool else if strings.hasprefix(str, "@shutdown")

// 列印輸入的字串

fmt.println(str)

return true

}

**說明如下:

4.程式入口

telnet回音處理主流程:

main.go

package main

import (

"os"

)func main()

**說明如下:

編譯所有**並執行,命令列提示如下:

listen: 127.0.0.1:7001
此時,socket偵聽成功。在作業系統中的命令列中輸入:

telnet 127.0.0.1 7001
嘗試連線本地的7001埠。接下來進入測試伺服器的流程。

5.測試輸入字串

在telnet連線後,輸入字串hello,telnet命令列顯示如下:

$ telnet 127.0.0.1 7001

trying 127.0.0.1...

connected to 127.0.0.1.

escape character is '^]'.

hello

hello

伺服器顯示如下:

listen: 127.0.0.1:7001

session started:

hello

客戶端輸入的字串會在伺服器中顯示,同時客戶端也會收到自己發給伺服器的內容,這就是一次回音。

6.測試關閉會話

當輸入@close時,telnet命令列顯示如下:

@close

connection closed by foreign host

伺服器顯示如下:

session closed
此時,客戶端telnet與伺服器斷開連線。

7.測試關閉伺服器

測試關閉伺服器

當輸入@shutdown時,telnet命令列顯示如下:

@shutdown

connection closed by foreign host.

伺服器顯示如下:

server shutdown
此時伺服器會自動關閉。

Go語言之併發程式設計(四)

同步 go 程式可以使用通道進行多個 goroutine 間的資料交換,但這僅僅是資料同步中的一種方法。通道內部的實現依然使用了各種鎖,因此優雅 的代價是效能。在某些輕量級的場合,原子訪問 atomic包 互斥鎖 sync.mutex 以及等待組 sync.waitgroup 能最大程度滿足需求。當...

Go語言之併發程式設計(一)

輕量級執行緒 goroutine 雖然,執行緒池為邏輯編寫者提供了執行緒分配的抽象機制。但是,如果面對隨時隨地可能發生的併發和執行緒處理需求,執行緒池就不是非常直觀和方便了。能否有一種機制 使用者分配足夠多的任務,系統能自動幫助使用者把任務分配到cpu上,讓這些任務盡量併發運作。這種機制在go語言中...

Go語言之Go語言網路程式設計

go語言的 net 包中有乙個 tcpconn 型別,可以用來建立 tcp 客戶端和 tcp 伺服器端間的通訊通道,tcpconn 型別裡有兩個主要的函式 func c tcpconn write b byte n int,err os.error func c tcpconn read b byt...