# quic-go trace code https://ithelp.ithome.com.tw/articles/10260277 如何import自己寫的package: 先創建資料夾,然後create a new module... https://linuxhint.com/golang-import-local-package/ https://go.dev/ref/mod https://go.dev/doc/code # quic-go ### Documentation: https://pkg.go.dev/github.com/lucas-clemente/quic-go#section-readme ### source-code: https://github.com/quic-go/quic-go failed to sufficiently increase receive buffer size. receiver buffer空間不夠,需要增加空間。 加這行指令: sysctl -w net.core.rmem_max=2500000 https://forum.storj.io/t/linux-failed-to-sufficiently-increase-receive-buffer-size/12494 https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Siz ### echo.go trace code: **operate stream**: io.Copy(dst Writer, src Reader): Copy copies from src to dst until either EOF is reached io.Reader: ``` type Reader interface { Read(p []byte) (n int, err error) } ``` Read reads up to len( p) bytes into p.(from stream) io.Writer: ``` type Writer interface { Write(p []byte) (n int, err error) } ``` Write writes len( p) bytes from p to the underlying data stream. Note:把io.Copy的程式複製到server後執行後發現,for loop不會break,這就是為什麼在io.Copy下面的code都執行不到的原因。for loop並不會執行很多次,而是到read時就卡住了,看起來是同步的,要等client再傳訊息才會往下跑。  一加break就報錯,所以stream io要在for裡執行? 但是client卻可以讀到資料 發現原因了,如果不在for裡執行,那server做完事就會關掉,這樣client就會出錯,所以把server一直開著直到client處理完即可。  所以用read write即可對stream進行操作 記得要sleep **net.Conn**: Conn is a generic stream-oriented network connection. **interface**: https://blog.kennycoder.io/2020/02/03/Golang-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3interface%E5%B8%B8%E8%A6%8B%E7%94%A8%E6%B3%95/ **netFD**: Network file descriptor. **UDPConn**: UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections. **ListenAddr**: ListenAddr creates a QUIC server listening on a given address. The tls.Config must not be nil and must contain a certificate configuration. The quic.Config may be nil, in that case the default values will be used. **net.ResolveUDPAddr**: ResolveUDPAddr returns an address of UDP end point. **chan struct{}**: 通常struct{}類型channel的用法是使用同步,一般不需要往channel裡面寫數據,只有讀等待,而讀等待會在channel被關閉的時候返回。 https://www.jianshu.com/p/7f45d7989f3a **select**: https://ithelp.ithome.com.tw/m/articles/10300162 **handlePacketImpl**: 處理封包(看起來應該是第一個封包),並決定buffer有無再繼續使用。 ### 各種interface的實做 **如何判斷interface 變量存儲的是哪種類型** 使用 value, ok := <variable>.(*<struct name>) 判斷  https://ithelp.ithome.com.tw/articles/10223932 https://go.dev/tour/methods/16 **使用go to implementations可以直接切到實作** go to implementations會直接找出所有那個interface的實作。 ## echoServer()  ### 1.call this function  listener 用baseServer實作(在listen func裡面)  handlePacke把封包寫到通道 s.run()接收receivedPackets裡的封包 查查看packet是如何被寫到receivedPackets的(瘋狂按reference): func (s *baseServer) handlePacket(p *receivedPacket) ⇦ func (h *packetHandlerMap) handlePacket(p *receivedPacket) ⇦ 這裡對封包做分類和處理 func (h *packetHandlerMap) listen() ⇦ 讀封包就是在這邊,只做一件事,聽封包  接收封包的struct,不知道kernel怎麼把資料送進去的 func newPacketHandlerMap ⇦  之後換找newPacketHandlerManager的reference func (m *connMultiplexer) AddConn ⇦  又回到這邊拉,所以一開始在做準備工作時,就有go routine去聽封包了。 AddConn不知道做了哪些事情 ### 2.Accept  回傳Connection interface **quicConn實例:** conn裡面是怎麼運作的? 看他放什麼東西到connQueue func (s *baseServer) handleNewConn(conn quicConn) ⇦  等待握手完成,回傳conn。 這裡產生conn並呼叫handleNewConn func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) ⇦ conn的生成就是在這邊 conn生成: 呼叫s.connHandler.AddWithConnID  這裡會到packetHandlerMap裡的handler[clientDestConnID]去取得packetHandler,如果失敗就生成一個加到handler[clientDestConnID]裡面,使用fn func生成。 fn的定義直接寫在使用的時候 conn:  如果added==0那就直接return,所以handleNewConn只會在Conn建立時執行一次。 func s.newConn:  內容太多了,要查再點進去看。 做一些初始設置,回傳connection。 這樣一來就能成功接收Conn了 前面的handleInitialImpl又是誰call的: func (s *baseServer) handlePacketImpl(p *receivedPacket) bool ⇦ 這裡解析封包,並做對應的處理。 這裡只處理長封包,短封包出現在這裡就錯了,可能用stream來處理吧,還沒看到。 func (s *baseServer) run() ⇦ ### 3 AcceptStream call this func  AcceptStream回傳stream **streamsMap:**   outgoingStreamsMap,這裡是struct定義,中間的[]是type parameter,用來指定可套用的interface https://itnext.io/how-to-use-golang-generics-with-structs-8cabc9353d75 回傳的Stream data type:  箭頭是新增的code,所以回傳的type是stream,找尋方法是看Stream的implement,然後一個一個試。 stream:  裡面還有receiveStream和sendStream,這樣組起來才實現了Stream interface receiveStream的Read實現了io.Read  再call readImpl call readImpl主要透過copy把資料讀進來 len(p)在過程中會變成有資料的長度(不知道為什麼),讓for loop可以break write實現:  ### 4 Copy call CopyBuffer  把stream當成Reader、Writer,使用Read、Write對他們進行操作 不是WriteTo、ReadTo,只用後面的Read、Write How stream is created?還沒trace How data is transfered? ## clientMain()  ### 1 最後呼叫dialContext,裡面有newClient,用來產生Client,然後回傳c.Conn,是quic 的connextion型態 client做好之後,dial可以製作裡面的conn  conn.run()做了handshake之後進runloop runloop似乎是在處理processedUndecryptablePacket?,已處理的無法解密封包 ### 2 OpenStreamSync opens a new bidirectional QUIC stream. It blocks until a new stream can be opened. If the error is non-nil, it satisfies the net.Error interface. If the connection was closed due to a timeout, Timeout() will be true. func OpenStreamSync ⇨ func (m *outgoingStreamsMap[T]) OpenStreamSync(ctx context.Context) ⇨ func openStream() ⇨  newStream是outgoingStreamsMap[T]裡的一個field func,不管查definition或implementation都不會有結果 這裡也不確定stream(T)是單向還是雙向,要再查。 newStream: 查看outgoingStreamsMap[T outgoingStream]怎麼製作的 connection內有streamsMap 查看connection 怎麼製作的 conn在client裡面 client做好之後,dial可以製作裡面的conn(用裡面的newClientConnection) conn裡的streamsMap定義在preSetup()裡面  ⇨ newStreamsMap  ⇨ initMap initMap把streamsMap裡的各種map都定義了(在streams_map.go裡面) ⇨ newStream golang中的struct 變數可以有一些欄位沒有初始化,那樣就設定成對應data type的初始值。 stream裡面還有receiveStream和sendStream分別處理read write stream:  receiveStream  sendStream  可以看出在stream中真正存放資料的是slice,chan只是用來做訊息控制
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up