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...