<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
從核心原理理解閘道器的本質
閘道器具備的基本功能:
藉助閘道器處理高可用,高並行
經典協定與封包
http 協定
GET/HTTP/1.1 Host:www.baidu.com User-Agent:curl/7.55.1 Accept:*/*
Websocket握手協定
三次握手的最主要的目的是保證連線是全雙工的,可靠更多的是通過重傳機制來保證的
因為連線是全雙工的,雙方必須都收到對方的FIN包及確認才可關閉
TCP報文格式:
其中比較重要的欄位有:
(1)序號(sequence number):Seq序號,佔32位元,用來標識從TCP源端向目的端傳送的位元組流,發起方傳送資料時對此進行標記。
(2)確認號(acknowledgement number):Ack序號,佔32位元,只有ACK標誌位為1時,確認序號欄位才有效,Ack=Seq+1。
(3)標誌位(Flags):共6個,即URG、ACK、PSH、RST、SYN、FIN等。具體含義如下:
URG:緊急指標(urgent pointer)有效。ACK:確認序號有效。PSH:接收方應該儘快將這個報文交給應用層。RST:重置連線。SYN:發起一個新連線。FIN:釋放一個連線。
需要注意的是:
不要將確認序號Ack與標誌位中的ACK搞混了。確認方Ack=發起方Seq+1,兩端配對。
三次握手連線:
(1)首先使用者端向伺服器端傳送一段TCP報文,其中:
(2)伺服器端接收到來自使用者端的TCP報文之後,結束LISTEN階段。並返回一段TCP報文,其中:
(3)使用者端接收到來自伺服器端的確認收到資料的TCP報文之後,明確了從使用者端到伺服器的資料傳輸是正常的,結束SYN-SENT階段。並返回最後一段TCP報文。其中:
伺服器收到來自使用者端的“確認收到伺服器資料”的TCP報文之後,明確了從伺服器到使用者端的資料傳輸是正常的。結束SYN-SENT階段,進入ESTABLISHED階段。
在使用者端與伺服器端傳輸的TCP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎上進行計算的,這樣做保證了TCP報文傳輸的連貫性。一旦出現某一方發出的TCP報文丟失,便無法繼續"握手",以此確保了"三次握手"的順利完成。
四次揮手:
(1)首先使用者端想要釋放連線,向伺服器端傳送一段TCP報文,其中:
(2)伺服器端接收到從使用者端發出的TCP報文之後,確認了使用者端想要釋放連線,隨後伺服器端結束ESTABLISHED階段,進入CLOSE-WAIT階段(半關閉狀態)並返回一段TCP報文,其中:
前"兩次揮手"既讓伺服器端知道了使用者端想要釋放連線,也讓使用者端知道了伺服器端了解了自己想要釋放連線的請求。於是,可以確認關閉使用者端到伺服器端方向上的連線了
(3)伺服器端自從發出ACK確認報文之後,經過CLOSED-WAIT階段,做好了釋放伺服器端到使用者端方向上的連線準備,再次向用戶端發出一段TCP報文,其中:
隨後伺服器端結束CLOSE-WAIT階段,進入LAST-ACK階段。並且停止在伺服器端到使用者端的方向上傳送資料,但是伺服器端仍然能夠接收從使用者端傳輸過來的資料。
(4)使用者端收到從伺服器端發出的TCP報文,確認了伺服器端已做好釋放連線的準備,結束FIN-WAIT-2階段,進入TIME-WAIT階段,並向伺服器端傳送一段報文,其中:
為什麼要使用者端要等待2MSL呢?見後文。
伺服器端收到從使用者端發出的TCP報文之後結束LAST-ACK階段,進入CLOSED階段。由此正式確認關閉伺服器端到使用者端方向上的連線。
使用者端等待完2MSL之後,結束TIME-WAIT階段,進入CLOSED階段,由此完成“四次揮手”。
後“兩次揮手”既讓使用者端知道了伺服器端準備好釋放連線了,也讓伺服器端知道了使用者端了解了自己準備好釋放連線了。於是,可以確認關閉伺服器端到使用者端方向上的連線了,由此完成“四次揮手”。
與“三次揮手”一樣,在使用者端與伺服器端傳輸的TCP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎上進行計算的,這樣做保證了TCP報文傳輸的連貫性,一旦出現某一方發出的TCP報文丟失,便無法繼續"揮手",以此確保了"四次揮手"的順利完成。
為什麼使用者端在TIME-WAIT階段要等2MSL?
當用戶端發出最後的ACK確認報文時,並不能確定伺服器端能夠收到該段報文。所以使用者端在傳送完ACK確認報文之後,會設定一個時長為2MSL的計時器。MSL指的是(最大的生命週期)Maximum Segment Lifetime:(30秒–1分鐘)一段TCP報文在傳輸過程中的最大生命週期。2MSL即是伺服器端發出為FIN報文和使用者端發出的ACK確認報文所能保持有效的最大時長。
伺服器端在1MSL內沒有收到使用者端發出的ACK確認報文,就會再次向用戶端發出FIN報文;
如果使用者端在2MSL內,再次收到了來自伺服器端的FIN報文,說明伺服器端由於各種原因沒有接收到使用者端發出的ACK確認報文。使用者端再次向伺服器端發出ACK確認報文,計時器重置,重新開始2MSL的計時;否則使用者端在2MSL內沒有再次收到來自伺服器端的FIN報文,說明伺服器端正常接收了ACK確認報文,使用者端可以進入CLOSED階段,完成“四次揮手”。
所以,使用者端要經歷時長為2SML的TIME-WAIT階段;這也是為什麼使用者端比伺服器端晚進入CLOSED階段的原因
func main() { //1、監聽埠 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %vn", err) return } //2.建立通訊端連線 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %vn", err) continue } //3. 建立處理協程 go func(conn net.Conn) { defer conn.Close() //思考題:這裡不填寫會有啥問題? //伺服器端就有一個close,wait狀態,使用者端就有一個finally 狀態 for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %vn", err) break } str := string(buf[:n]) fmt.Printf("receive from client, data: %vn", str) } }(conn) } }
TCP為啥需要流量控制
優化步驟3到步驟4:因為網路擁塞,有24直接降到1 ,會造成堵塞
粘包、拆包表現形式
現在假設使用者端向伺服器端連續傳送了兩個封包,用packet1和packet2來表示,那麼伺服器端收到的資料可以分為三種,現列舉如下:
第一種情況,接收端正常收到兩個封包,即沒有發生拆包和粘包的現象,此種情況不在本文的討論範圍內。
第二種情況,接收端只收到一個封包,由於TCP是不會出現丟包的,所以這一個封包中包含了傳送端傳送的兩個封包的資訊,這種現象即為粘包。這種情況由於接收端不知道這兩個封包的界限,所以對於接收端來說很難處理。
第三種情況,這種情況有兩種表現形式,如下圖。接收端收到了兩個封包,但是這兩個封包要麼是不完整的,要麼就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對於接收端同樣是不好處理的。
我們知道tcp是以流動的方式傳輸資料,傳輸的最小單位為一個報文段(segment)。tcp Header中有個Options標識位,常見的標識為mss(Maximum Segment Size)指的是,連線層每次傳輸的資料有個最大限制MTU(Maximum Transmission Unit),一般是1500位元,超過這個量要分成多個報文段,mss則是這個最大限制減去TCP的header,光是要傳輸的資料的大小,一般為1460位元。換算成位元組,也就是180多位元組。
tcp為提高效能,傳送端會將需要傳送的資料傳送到緩衝區,等待緩衝區滿了之後,再將緩衝中的資料傳送到接收方。同理,接收方也有緩衝區這樣的機制,來接收資料。
發生TCP粘包、拆包主要是由於下面一些原因:
既然知道了tcp是無界的資料流,且協定本身無法避免粘包,拆包的發生,那我們只能在應用層資料協定上,加以控制。通常在制定傳輸資料時,可以使用如下方法:
如何獲取完整的資料包文
func main() { //類比接收緩衝區 bytesBuffer := bytes.NewBuffer([]byte{}) // 傳送 if err := Encode(bytesBuffer, "hello world 0 !!"); err != nil { panic(err) } if err := Encode(bytesBuffer, "hello world 1 !!"); err != nil { panic(err) } //讀取 for { if bt, err := Decode(bytesBuffer); err == nil { fmt.Println(string(bt)) continue } break } }
如何獲取完整的資料包文
tcp_server
func main() { //simple tcp server //1.監聽埠 listener, err := net.Listen("tcp", "127.0.0.1:9090") if err != nil { fmt.Printf("tcp Listen fail,err: %vn", err) return } //2.接受請求 for { conn, err := listener.Accept() if err != nil { fmt.Printf("tcp Accept fail,err: %vn", err) continue } //3.建立協程 go process(conn) } } //4.建立的協程裡面實現解碼的功能 func process(conn net.Conn) { defer conn.Close() for { bt, err := unpack.Decode(conn) if err != nil { fmt.Printf("read from connect failed, err: %vn", err) break } str := string(bt) fmt.Printf("receive from client, data: %vn", str) } }
tcp_client
func main() { //1.連線tcp伺服器 conn, err := net.Dial("tcp", "localhost:9090") defer conn.Close() if err != nil { fmt.Printf("connect failed, err : %vn", err.Error()) return } //2.實現編碼 unpack.Encode(conn, "hello world 0!!!") }
**unpack ** : 實現編碼(encode)和解碼(docode)功能
const Msg_Header = "12345678" // 編碼 func Encode(bytesBuffer io.Writer, content string) error { //msg_header+content_len+content //8+4+content_len if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(Msg_Header)); err != nil { return err } clen := int32(len([]byte(content))) // binary.BigEndian 大端位元組實現的加密 , if err := binary.Write(bytesBuffer, binary.BigEndian, clen); err != nil { return err } if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(content)); err != nil { return err } return nil } // 解碼 func Decode(bytesBuffer io.Reader) (bodyBuf []byte, err error) { MagicBuf := make([]byte, len(Msg_Header)) //先讀取header的大小 if _, err = io.ReadFull(bytesBuffer, MagicBuf); err != nil { return nil, err } //比較得到的header和實際的Msg_Header 是否相同 if string(MagicBuf) != Msg_Header { return nil, errors.New("msg_header error") } lengthBuf := make([]byte, 4) if _, err = io.ReadFull(bytesBuffer, lengthBuf); err != nil { return nil, err } // binary.BigEndian 大端位元組實現的解密 ,得到實際資料的長度 length := binary.BigEndian.Uint32(lengthBuf) bodyBuf = make([]byte, length) if _, err = io.ReadFull(bytesBuffer, bodyBuf); err != nil { return nil, err } return bodyBuf, err }
golang 實現UDP 伺服器端與使用者端
UDP伺服器端:
func main() { //1.監聽埠 listen, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 9090, }) if err != nil { fmt.Printf("listen udp failed ,err:%vn", err) return } //2.迴圈讀取訊息內容 for { var data [1024]byte n, addr, err := listen.ReadFromUDP(data[:]) if err != nil { fmt.Printf("read failed from addr :%v,err%vn", addr, err) break } go func() { //3.回覆資料 fmt.Printf("addr:%v data:%v count:%vn", addr, string(data[:n]), n) _, err = listen.WriteToUDP([]byte("received success!"), addr) if err != nil { fmt.Printf("write failed,err :%vn", err) return } }() } }
udp使用者端
func main() { //1. 連線udp伺服器 conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 9090, }) if err != nil { fmt.Printf("connect failed ,err %vn", err) return } for i := 0; i < 100; i++ { // 2.傳送資料 _, err := conn.Write([]byte("hello " + "server")) if err != nil { fmt.Printf("send data failed,err: %vn", err) return } // 3. 接收資料 result := make([]byte, 1024) n, remoteAddr, err := conn.ReadFromUDP(result) if err != nil { fmt.Printf("read data failed,err:%vn", err) return } fmt.Printf("receive from addr:%v data:%vn", remoteAddr, string(result[:n])) } }
golang實現tcp的伺服器端和使用者端
tcp 伺服器端
func main() { //1、監聽埠 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %vn", err) return } //2.建立通訊端連線 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %vn", err) continue } //3. 建立處理協程 go process(conn) } } func process(conn net.Conn) { defer conn.Close() //思考題:這裡不填寫會有啥問題? for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %vn", err) break } str := string(buf[:n]) fmt.Printf(" from client, data: %vn", str) } }
tcp使用者端
golang實現Http的伺服器端和使用者端
http伺服器端
var ( Addr = ":8000" ) // http的伺服器 func main() { //1.建立路由器 mux := http.NewServeMux() // 2. 設定路由規則 mux.HandleFunc("/bye", sayBye) // 3.建立伺服器 server := &http.Server{ Addr: Addr, WriteTimeout: time.Second * 3, Handler: mux, } // 4. 監聽埠並提供服務 log.Println("starting httpServer at" + Addr) log.Fatal(server.ListenAndServe()) } func sayBye(w http.ResponseWriter, r *http.Request) { time.Sleep(1 * time.Second) w.Write([]byte("bye bye,this is httpserver")) }
http使用者端
func main() { //1. 建立連線池 transport := &http.Transport{ DialContext: (&net.Dialer{ Timeout: 30 * time.Second, // 超時時間 KeepAlive: 30 * time.Second, //長連線時間 }).DialContext, MaxIdleConns: 100, //最大空閒連線數 IdleConnTimeout: 90 * time.Second, // 空閒超時時間 TLSHandshakeTimeout: 10 * time.Second, // tls握手超時時間 ExpectContinueTimeout: 1 * time.Second, // 100-continue 狀態碼超時時間 } //2. 建立使用者端 client := &http.Client{ Timeout: 30 * time.Second, Transport: transport, } //3.請求資料 resp, err := client.Get("http://127.0.0.1:8000/bye") if err != nil { fmt.Println("client get url failed ", err) return } defer resp.Body.Close() //4.讀取內容 b, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Read body failed ", err) return } fmt.Println(string(b)) }
Http 伺服器原始碼解讀
函數是一等公民
type HandleFunc func(http.ResponseWriter, *http.Request) func (f HandleFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) } //函數是一等公民 func main() { hf := HandleFunc(HelloHandler) resp := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", bytes.NewBuffer([]byte("test"))) hf.ServerHTTP(resp, req) b, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(b)) } func HelloHandler(res http.ResponseWriter, req *http.Request) { res.Write([]byte("hello youMe ")) }
到此這篇關於Go微服務閘道器的實現的文章就介紹到這了,更多相關Go微服務閘道器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45