您好,登錄后才能下訂單哦!
今天小編給大家分享一下GO語(yǔ)言如何實(shí)現(xiàn)TCP服務(wù)器的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
interface/tcp/Handler.go
type Handler interface { Handle(ctx context.Context, conn net.Conn) Close() error }
Handler:業(yè)務(wù)邏輯的處理接口
Handle(ctx context.Context, conn net.Conn) 處理連接
tcp/server.go
type Config struct { Address string } func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error { closeChan := make(chan struct{}) listen, err := net.Listen("tcp", cfg.Address) if err != nil { return err } logger.Info("start listen") ListenAndServe(listen, handler, closeChan) return nil } func ListenAndServe(listener net.Listener, handler tcp.Handler, closeChan <-chan struct{}) { ctx := context.Background() var waitDone sync.WaitGroup for true { conn, err := listener.Accept() if err != nil { break } logger.Info("accept link") waitDone.Add(1) go func() { defer func() { waitDone.Done() }() handler.Handler(ctx, conn) }() } waitDone.Wait() }
Config:?jiǎn)?dòng)tcp服務(wù)器的配置
Address:監(jiān)聽地址
ListenAndServe:ctx是上下文,可以傳遞一些參數(shù)。死循環(huán)中接收到新連接時(shí),讓一個(gè)協(xié)程去處理連接
如果listener.Accept()出錯(cuò)了就會(huì)break跳出來,這時(shí)候需要等待已經(jīng)服務(wù)的客戶端退出。使用WaitGroup等待客服端退出
func ListenAndServe(listener net.Listener, handler tcp.Handler, closeChan <-chan struct{}) { go func() { <-closeChan logger.Info("shutting down...") _ = listener.Close() _ = handler.Close() }() defer func() { _ = listener.Close() _ = handler.Close() }() ...... }
listener和handler在退出的時(shí)候需要關(guān)掉。如果用戶直接kill掉了程序,我們也需要關(guān)掉listener和handler,這時(shí)候要使用closeChan,一旦接收到關(guān)閉信號(hào),就執(zhí)行關(guān)閉邏輯
func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error { closeChan := make(chan struct{}) sigCh := make(chan os.Signal) signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { sig := <-sigCh switch sig { case syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: closeChan <- struct{}{} } }() listen, err := net.Listen("tcp", cfg.Address) if err != nil { return err } logger.Info("start listen") ListenAndServe(listen, handler, closeChan) return nil }
當(dāng)系統(tǒng)對(duì)程序發(fā)送信號(hào)時(shí),sigCh會(huì)接收到信號(hào)
tcp/echo.go
type EchoHandler struct { activeConn sync.Map closing atomic.Boolean }
EchoHandler:
activeConn:記錄連接
closing:是否正在關(guān)閉,有并發(fā)競(jìng)爭(zhēng),使用atomic.Boolean
type EchoClient struct { Conn net.Conn Waiting wait.Wait } func (c *EchoClient) Close() error { c.Waiting.WaitWithTimeout(10 * time.Second) _ = c.Conn.Close() return nil }
EchoClient:一個(gè)客戶端就是一個(gè)連接。Close方法關(guān)閉客戶端連接,超時(shí)時(shí)間設(shè)置為10s
func MakeHandler() *EchoHandler { return &EchoHandler{} } func (h *EchoHandler) Handle(ctx context.Context, conn net.Conn) { // 連接正在關(guān)閉,不接收新連接 if h.closing.Get() { _ = conn.Close() } client := &EchoClient{ Conn: conn, } h.activeConn.Store(client, struct{}{}) reader := bufio.NewReader(conn) for { msg, err := reader.ReadString('\n') if err != nil { if err == io.EOF { logger.Info("connection close") h.activeConn.Delete(client) } else { logger.Warn(err) } return } // 正在處理業(yè)務(wù),不要關(guān)掉 client.Waiting.Add(1) // 將數(shù)據(jù)原封不動(dòng)寫回去,測(cè)試 b := []byte(msg) _, _ = conn.Write(b) client.Waiting.Done() } } func (h *EchoHandler) Close() error { logger.Info("handler shutting down...") h.closing.Set(true) h.activeConn.Range(func(key interface{}, val interface{}) bool { client := key.(*EchoClient) _ = client.Close() return true }) return nil }
MakeEchoHandler:創(chuàng)建EchoHandler
Handle:處理客戶端的連接。
1.連接正在關(guān)閉時(shí),不接收新連接
2.存儲(chǔ)新連接,value用空結(jié)構(gòu)體
3.使用緩存區(qū)接收用戶發(fā)來的數(shù)據(jù),使用\n作為結(jié)束的標(biāo)志
Close:將所有客戶端連接關(guān)掉
main.go
const configFile string = "redis.conf" var defaultProperties = &config.ServerProperties{ Bind: "0.0.0.0", Port: 6379, } func fileExists(filename string) bool { info, err := os.Stat(filename) return err == nil && !info.IsDir() } func main() { logger.Setup(&logger.Settings{ Path: "logs", Name: "godis", Ext: "log", TimeFormat: "2022-02-02", }) if fileExists(configFile) { config.SetupConfig(configFile) } else { config.Properties = defaultProperties } err := tcp.ListenAndServeWithSignal( &tcp.Config{ Address: fmt.Sprintf("%s:%d", config.Properties.Bind, config.Properties.Port), }, EchoHandler.MakeHandler()) if err != nil { logger.Error(err) } }
以上就是“GO語(yǔ)言如何實(shí)現(xiàn)TCP服務(wù)器”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。