亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

利用Golang實現TCP連接的雙向拷貝詳解

2020-04-01 19:03:30
字體:
來源:轉載
供稿:網友

前言

本文主要給大家介紹了關于Golang實現TCP連接的雙向拷貝的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

最簡單的實現

每次來一個Server的連接,就新開一個Client的連接。用一個goroutine從server拷貝到client,再用另外一個goroutine從client拷貝到server。任何一方斷開連接,雙向都斷開連接。

func main() { runtime.GOMAXPROCS(1) listener, err := net.Listen("tcp", "127.0.0.1:8848") if err != nil { panic(err) } for { conn, err := listener.Accept() if err != nil { panic(err) } go handle(conn.(*net.TCPConn)) }}func handle(server *net.TCPConn) { defer server.Close() client, err := net.Dial("tcp", "127.0.0.1:8849") if err != nil { fmt.Print(err) return } defer client.Close() go func() { defer server.Close() defer client.Close() buf := make([]byte, 2048) io.CopyBuffer(server, client, buf) }() buf := make([]byte, 2048) io.CopyBuffer(client, server, buf)}

一個值得注意的地方是io.Copy的默認buffer比較大,給一個小的buffer可以支持更多的并發連接。

這兩個goroutine并序在一個退出之后,另外一個也退出。這個的實現是通過關閉server或者client的socket來實現的。因為socket被關閉了,io.CopyBuffer 就會退出。

Client端實現連接池

一個顯而易見的問題是,每次Server的連接進來之后都需要臨時去建立一個新的Client的端的連接。這樣在代理的總耗時里就包括了一個tcp連接的握手時間。如果能夠讓Client端實現連接池復用已有連接的話,可以縮短端到端的延遲。

var pool = make(chan net.Conn, 100)func borrow() (net.Conn, error) { select { case conn := <- pool: return conn, nil default: return net.Dial("tcp", "127.0.0.1:8849") }}func release(conn net.Conn) error { select { case pool <- conn: // returned to pool return nil default: // pool is overflow return conn.Close() }}func handle(server *net.TCPConn) { defer server.Close() client, err := borrow() if err != nil { fmt.Print(err) return } defer release(client) go func() { defer server.Close() defer release(client) buf := make([]byte, 2048) io.CopyBuffer(server, client, buf) }() buf := make([]byte, 2048) io.CopyBuffer(client, server, buf)}

這個版本的實現是顯而易見有問題的。因為連接在歸還到池里的時候并不能保證是還保持連接的狀態。另外一個更嚴重的問題是,因為client的連接不再被關閉了,當server端關閉連接時,從client向server做io.CopyBuffer的goroutine就無法退出了。

所以,有以下幾個問題要解決:

  • 如何在一個goroutine時退出時另外一個goroutine也退出?
  • 怎么保證歸還給pool的連接是有效的?
  • 怎么保持在pool中的連接仍然是一直有效的?

通過SetDeadline中斷Goroutine

一個普遍的觀點是Goroutine是無法被中斷的。當一個Goroutine在做conn.Read時,這個協程就被阻塞在那里了。實際上并不是毫無辦法的,我們可以通過conn.Close來中斷Goroutine。但是在連接池的情況下,又無法Close鏈接。另外一種做法就是通過SetDeadline為一個過去的時間戳來中斷當前正在進行的阻塞讀或者阻塞寫。

var pool = make(chan net.Conn, 100)type client struct { conn net.Conn inUse *sync.WaitGroup}func borrow() (clt *client, err error) { var conn net.Conn select { case conn = <- pool: default: conn, err = net.Dial("tcp", "127.0.0.1:18849") } if err != nil { return nil, err } clt = &client{ conn: conn, inUse: &sync.WaitGroup{}, } return}func release(clt *client) error { clt.conn.SetDeadline(time.Now().Add(-time.Second)) clt.inUse.Done() clt.inUse.Wait() select { case pool <- clt.conn: // returned to pool return nil default: // pool is overflow return clt.conn.Close() }}func handle(server *net.TCPConn) { defer server.Close() clt, err := borrow() if err != nil { fmt.Print(err) return } clt.inUse.Add(1) defer release(clt) go func() { clt.inUse.Add(1) defer server.Close() defer release(clt) buf := make([]byte, 2048) io.CopyBuffer(server, clt.conn, buf) }() buf := make([]byte, 2048) io.CopyBuffer(clt.conn, server, buf)}

通過SetDeadline實現了goroutine的中斷,然后通過sync.WaitGroup來保證這些使用方都退出了之后再歸還給連接池。否則一個連接被復用的時候,之前的使用方可能還沒有退出。

連接有效性

為了保證在歸還給pool之前,連接仍然是有效的。連接在被讀寫的過程中如果發現了error,我們就要標記這個連接是有問題的,會釋放之后直接close掉。但是SetDeadline必然會導致讀取或者寫入的時候出現一次timeout的錯誤,所以還需要把timeout排除掉。

var pool = make(chan net.Conn, 100)type client struct { conn net.Conn inUse *sync.WaitGroup isValid int32}const maybeValid = 0const isValid = 1const isInvalid = 2func (clt *client) Read(b []byte) (n int, err error) { n, err = clt.conn.Read(b) if err != nil { if !isTimeoutError(err) { atomic.StoreInt32(&clt.isValid, isInvalid) } } else { atomic.StoreInt32(&clt.isValid, isValid) } return}func (clt *client) Write(b []byte) (n int, err error) { n, err = clt.conn.Write(b) if err != nil { if !isTimeoutError(err) { atomic.StoreInt32(&clt.isValid, isInvalid) } } else { atomic.StoreInt32(&clt.isValid, isValid) } return}type timeoutErr interface { Timeout() bool}func isTimeoutError(err error) bool { timeoutErr, _ := err.(timeoutErr) if timeoutErr == nil { return false } return timeoutErr.Timeout()}func borrow() (clt *client, err error) { var conn net.Conn select { case conn = <- pool: default: conn, err = net.Dial("tcp", "127.0.0.1:18849") } if err != nil { return nil, err } clt = &client{ conn: conn, inUse: &sync.WaitGroup{}, isValid: maybeValid, } return}func release(clt *client) error { clt.conn.SetDeadline(time.Now().Add(-time.Second)) clt.inUse.Done() clt.inUse.Wait() if clt.isValid == isValid { return clt.conn.Close() } select { case pool <- clt.conn: // returned to pool return nil default: // pool is overflow return clt.conn.Close() }}func handle(server *net.TCPConn) { defer server.Close() clt, err := borrow() if err != nil { fmt.Print(err) return } clt.inUse.Add(1) defer release(clt) go func() { clt.inUse.Add(1) defer server.Close() defer release(clt) buf := make([]byte, 2048) io.CopyBuffer(server, clt, buf) }() buf := make([]byte, 2048) io.CopyBuffer(clt, server, buf)}

判斷 error 是否是 timeout 需要類型強轉來實現。

對于連接池里的conn是否仍然是有效的,如果用后臺不斷ping的方式來實現成本比較高。因為不同的協議要連接保持需要不同的ping的方式。一個最簡單的辦法就是下次用的時候試一下。如果連接不好用了,則改成新建一個連接,避免連續拿到無效的連接。通過這種方式把無效的連接給淘汰掉。

關于正確性

本文在杭州機場寫成,完全不保證內容的正確性

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产成+人+综合+亚洲欧美丁香花| 日韩在线观看免费全集电视剧网站| 在线日韩av观看| 91在线直播亚洲| 亚洲午夜性刺激影院| 亚洲精品suv精品一区二区| 精品国产成人在线| 久久69精品久久久久久久电影好| 亚洲一二三在线| 亚洲国产小视频在线观看| 欧美理论在线观看| 92看片淫黄大片欧美看国产片| 久久夜精品va视频免费观看| 5566日本婷婷色中文字幕97| 欧美最猛性xxxx| 久久人91精品久久久久久不卡| 亚洲精品一区中文| 欧美高清激情视频| 久久久女女女女999久久| 欧美有码在线视频| 亚洲精品ady| 欧美黄色性视频| 日韩成人在线播放| 国产97在线|亚洲| 欧美性猛交xxxx富婆弯腰| 久久精品在线视频| 国产成人精品电影久久久| 中文字幕欧美精品在线| 久久精品国产欧美激情| 久久久这里只有精品视频| 中文字幕一区日韩电影| 国产精品一区二区三区毛片淫片| 欧美日韩亚洲精品内裤| 91精品视频在线看| 亚洲第一黄色网| 日韩av在线网站| 国产精品日本精品| 91亚洲精品久久久久久久久久久久| 亚洲第一黄色网| 91九色国产视频| 久久精品国产免费观看| 国产精品网红福利| 日韩欧美在线观看| 久久久国产视频| 成人免费xxxxx在线观看| 国产日韩综合一区二区性色av| 欧美野外wwwxxx| 国产精品第三页| 丝袜亚洲欧美日韩综合| 亚洲精品国产精品国自产观看浪潮| 伊人久久五月天| 国产精品久久久久福利| 日本道色综合久久影院| 欧美最顶级丰满的aⅴ艳星| 91精品久久久久久久| 亚洲人成亚洲人成在线观看| 亚洲欧美成人一区二区在线电影| 午夜美女久久久久爽久久| 久久精品国产成人精品| 亚洲男人的天堂在线播放| 久久久久久国产| 亚洲男女自偷自拍图片另类| 两个人的视频www国产精品| 久久精品国产一区二区三区| www.日韩视频| 久久久精品网站| 伊人av综合网| 伊人一区二区三区久久精品| 欧美巨猛xxxx猛交黑人97人| 亚洲欧美在线磁力| 中文字幕亚洲欧美一区二区三区| 搡老女人一区二区三区视频tv| 国产高清在线不卡| 热久久免费视频精品| 91色中文字幕| 91精品国产综合久久香蕉922| 中文字幕欧美视频在线| 日本久久久久久久久| 美女性感视频久久久| 国产精品扒开腿做爽爽爽视频| 欧美性生交大片免费| 国产精品影片在线观看| 中文字幕在线看视频国产欧美在线看完整| 亚洲嫩模很污视频| 日韩精品在线私人| 亚洲第一在线视频| 亚洲精品福利在线| 97成人精品视频在线观看| 亚洲护士老师的毛茸茸最新章节| 久久综合九色九九| 成人黄色av网站| 亚洲a级在线播放观看| 久久久亚洲国产天美传媒修理工| 欧美人与性动交a欧美精品| 国产欧美日韩中文| 亚洲视频欧美视频| 性色av香蕉一区二区| 88xx成人精品| 国产精品欧美风情| 中文字幕精品影院| 中文字幕欧美国内| 国产成人精品av| 日韩av电影手机在线观看| 国外成人在线直播| 久久久久免费视频| 久久av红桃一区二区小说| 青青青国产精品一区二区| 中文字幕国产日韩| 亚洲精品99久久久久中文字幕| 国产精品成熟老女人| 国产成人亚洲综合91精品| 久久综合久久八八| 亚洲精品电影网站| 亚洲a在线观看| 亚洲视频999| 欧美激情亚洲综合一区| 欧美激情精品久久久久久蜜臀| 狠狠躁18三区二区一区| 日韩电影中文字幕在线观看| 久久久久在线观看| 中文字幕免费国产精品| 久久久精品999| 久久国产精品免费视频| 久久视频免费在线播放| 精品亚洲一区二区| 欧美日韩黄色大片| 91国在线精品国内播放| 欧美在线一级va免费观看| 久久6精品影院| 国产日韩精品视频| 日韩电影第一页| 欧美性videos高清精品| 91久久国产精品91久久性色| 久久久精品在线| 亚洲欧美成人网| 国产精品高潮呻吟视频| 国产精品久久久久久久av电影| 久久精品影视伊人网| 日韩欧美精品中文字幕| 2019精品视频| 亚洲精品福利资源站| 成人h视频在线| 中文字幕精品一区二区精品| 国产精品美乳在线观看| 欧美激情手机在线视频| 法国裸体一区二区| 日韩久久免费视频| 一区二区成人av| 神马国产精品影院av| 久久69精品久久久久久久电影好| 欧美日韩免费网站| 久久精品国产成人精品| 91成人天堂久久成人| 久久99精品久久久久久青青91| 日韩精品极品毛片系列视频| 日韩一区二区三区国产| 国产精品入口免费视| 国产精品999| 亚洲黄色有码视频| 国产精品欧美日韩一区二区| 成人免费福利视频| 中文字幕亚洲第一| 久久精品视频在线播放| 成人国产亚洲精品a区天堂华泰|