网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
return
}
a.ret = a.ret[:0]
// 4元组组成的key
key := key{netFlow, t.TransportFlow()}
var conn *connection
// This for loop handles a race condition where a connection will close, lock
// the connection pool, and remove itself, but before it locked the connection
// pool it's returned to another Assemble statement. This should loop 0-1
// times for the VAST majority of cases.
// 创建conn
for {
// tcp keepalive syn=0 payload=0
// 即 true && true end 为true?
conn = a.connPool.getConnection(key, !t.SYN && len(t.LayerPayload()) == 0, timestamp)
if conn == nil {
if *debugLog {
log.Printf("%v got empty packet on otherwise empty connection", key)
}
return
}
conn.mu.Lock()
if !conn.closed {
break
}
conn.mu.Unlock()
}
if conn.lastSeen.Before(timestamp) {
conn.lastSeen = timestamp
}
//type Sequence int64 提供Difference和Add函数
seq, bytes := Sequence(t.Seq), t.Payload // seq:当前序号 bytes:tcp负载的数据
// 校验序号
if conn.nextSeq == invalidSequence {
if t.SYN {
if *debugLog {
log.Printf("%v saw first SYN packet, returning immediately, seq=%v", key, seq)
}
// 添加 Reassembly重组后的对象
a.ret = append(a.ret, Reassembly{
Bytes: bytes,
Skip: 0,
Start: true,
Seen: timestamp,
})
// 下一个包的序号 = 当前的序号 + 字节数 + 1
conn.nextSeq = seq.Add(len(bytes) + 1)
} else {
if *debugLog {
log.Printf("%v waiting for start, storing into connection", key)
}
// 插入到数据到connection中
a.insertIntoConn(t, conn, timestamp)
}
} else if diff := conn.nextSeq.Difference(seq); diff > 0 {
if *debugLog {
log.Printf("%v gap in sequence numbers (%v, %v) diff %v, storing into connection", key, conn.nextSeq, seq, diff)
}
// 插入到数据到connection中
a.insertIntoConn(t, conn, timestamp)
} else {=<0
// 字节校准
bytes, conn.nextSeq = byteSpan(conn.nextSeq, seq, bytes)
if *debugLog {
log.Printf("%v found contiguous data (%v, %v), returning immediately", key, seq, conn.nextSeq)
}
a.ret = append(a.ret, Reassembly{
Bytes: bytes,
Skip: 0,
End: t.RST || t.FIN,
Seen: timestamp,
})
}
if len(a.ret) > 0 {
a.sendToConnection(conn)
}
conn.mu.Unlock()
}
### insertIntoConn
func (a *Assembler) insertIntoConn(t *layers.TCP, conn *connection, ts time.Time) {
if conn.first != nil && conn.first.seq == conn.nextSeq {
panic(“wtf”)
}
// p:第一页 p2:最后一页 numPages:页数
p, p2, numPages := a.pagesFromTCP(t, ts)
//遍历双向链接page列表获取正确的放置给定序号的位置
// 直接插入不好吗?
prev, current := conn.traverseConn(Sequence(t.Seq))
conn.pushBetween(prev, current, p, p2)
conn.pages += numPages
// 校验最大缓冲page数
if (a.MaxBufferedPagesPerConnection > 0 && conn.pages >= a.MaxBufferedPagesPerConnection) ||
(a.MaxBufferedPagesTotal > 0 && a.pc.used >= a.MaxBufferedPagesTotal) {
if *debugLog {
log.Printf("%v hit max buffer size: %+v, %v, %v", conn.key, a.AssemblerOptions, conn.pages, a.pc.used)
}
// 弹出
a.addNextFromConn(conn)
}
}
### pagesFromTCP
从TCP数据包创建一个page(或设置一个pages)。
注意此函数不应该接受SYN包,因为它不能正确处理seq。
返回双连接的page列表中的第一个和最后一个页面。
func (a *Assembler) pagesFromTCP(t *layers.TCP, ts time.Time) (p, p2 *page, numPages int) {
first := a.pc.next(ts)
current := first
numPages++
seq, bytes := Sequence(t.Seq), t.Payload
for {
length := min(len(bytes), pageBytes)
// 拷贝负载数据
current.Bytes = current.buf[:length]
copy(current.Bytes, bytes)
// 设置seq
current.seq = seq
// 处理剩余数据>1900,一般不会进入到这里,实际场景下MTU会将TCP切段
bytes = bytes[length:]
if len(bytes) == 0 {
break
}
seq = seq.Add(length)
// 创建下一页
current.next = a.pc.next(ts)
// 设置下一个的prev为current
current.next.prev = current
// 设置下一页
current = current.next
numPages++
}
current.End = t.RST || t.FIN // 设置end
return first, current, numPages
}
### addNextFromConn
弹出第一页
func (a *Assembler) addNextFromConn(conn *connection) {
if conn.nextSeq == invalidSequence {
conn.first.Skip = -1
} else if diff := conn.nextSeq.Difference(conn.first.seq); diff > 0 {
conn.first.Skip = int(diff)
}
conn.first.Bytes, conn.nextSeq = byteSpan(conn.nextSeq, conn.first.seq, conn.first.Bytes)
if *debugLog {
log.Printf(“%v adding from conn (%v, %v)”, conn.key, conn.first.seq, conn.nextSeq)
}
a.ret = append(a.ret, conn.first.Reassembly)
a.pc.replace(conn.first)
if conn.first == conn.last {
conn.first = nil
conn.last = nil
} else {
conn.first = conn.first.next
conn.first.prev = nil
}
conn.pages–
}
### sendToConnection
func (a *Assembler) sendToConnection(conn *connection) {
// 组数据
a.addContiguous(conn)
if conn.stream == nil {
panic(“why?”)
}
conn.stream.Reassembled(a.ret)
if a.ret[len(a.ret)-1].End {
a.closeConnection(conn)
}
}
### addContiguous
func (a *Assembler) addContiguous(conn *connection) {
for conn.first != nil && conn.nextSeq.Difference(conn.first.seq) <= 0 {
a.addNextFromConn(conn)
}
}
### addNextFromConn
弹出第一页添加到数组中
func (a *Assembler) addNextFromConn(conn *connection) {
if conn.nextSeq == invalidSequence {
conn.first.Skip = -1
} else if diff := conn.nextSeq.Difference(conn.first.seq); diff > 0 {
conn.first.Skip = int(diff)
}
conn.first.Bytes, conn.nextSeq = byteSpan(conn.nextSeq, conn.first.seq, conn.first.Bytes)
if *debugLog {
log.Printf(“%v adding from conn (%v, %v)”, conn.key, conn.first.seq, conn.nextSeq)
}
a.ret = append(a.ret, conn.first.Reassembly)
a.pc.replace(conn.first)
if conn.first == conn.last {
conn.first = nil
conn.last = nil
} else {
conn.first = conn.first.next
conn.first.prev = nil
}
conn.pages–
}
### closeConnection
func (a *Assembler) closeConnection(conn *connection) {
if *debugLog {
log.Printf(“%v closing”, conn.key)
}
conn.stream.ReassemblyComplete()
conn.closed = true
a.connPool.remove(conn)
for p := conn.first; p != nil; p = p.next {
a.pc.replace§
}
}
## StreamPool
管理流的连接池,初始连接池分配1024个
type StreamPool struct {
conns map[key]*connection
users int
mu sync.RWMutex
factory StreamFactory
free []*connection
all [][]connection
nextAlloc int
newConnectionCount int64
}
func NewStreamPool(factory StreamFactory) *StreamPool {
return &StreamPool{
conns: make(map[key]*connection, initialAllocSize),
free: make([]*connection, 0, initialAllocSize),
factory: factory,
nextAlloc: initialAllocSize,
}
}
### grow
分配连接
func (p *StreamPool) grow() {
conns := make([]connection, p.nextAlloc)
p.all = append(p.all, conns)
for i := range conns {
p.free = append(p.free, &conns[i])
}
if *memLog {
log.Println(“StreamPool: created”, p.nextAlloc, “new connections”)
}
p.nextAlloc *= 2
}
### newConnection
创建连接
func (p *StreamPool) newConnection(k key, s Stream, ts time.Time) (c *connection) {
if *memLog {
p.newConnectionCount++
if p.newConnectionCount&0x7FFF == 0 {
log.Println(“StreamPool:”, p.newConnectionCount, “requests,”, len(p.conns), “used,”, len(p.free), “free”)
}
}
if len(p.free) == 0 {
p.grow()
}
index := len(p.free) - 1
c, p.free = p.free[index], p.free[:index]
c.reset(k, s, ts)
return c
}
### getConnection
// 返回一个连接,如果连接已经被关闭或者连接不存在,返回nil
func (p *StreamPool) getConnection(k key, end bool, ts time.Time) *connection {
p.mu.RLock()
conn := p.conns[k]
p.mu.RUnlock()
if end || conn != nil {
return conn
}
s := p.factory.New(k[0], k[1])
p.mu.Lock()
conn = p.newConnection(k, s, ts)
if conn2 := p.conns[k]; conn2 != nil {
p.mu.Unlock()
return conn2
}
p.conns[k] = conn
p.mu.Unlock()
return conn
}
### remove
删除某个个连接
func (p *StreamPool) remove(conn *connection) {
p.mu.Lock()
delete(p.conns, conn.key)
p.free = append(p.free, conn)
p.mu.Unlock()
}
### connection
返回所有的连接
func (p *StreamPool) connections() []*connection {
p.mu.RLock()
conns := make([]*connection, 0, len(p.conns))
for _, conn := range p.conns {
conns = append(conns, conn)
}
p.mu.RUnlock()
return conns
}
## connection
type connection struct {
key key
pages int
first, last *page
nextSeq Sequence
created, lastSeen time.Time
stream Stream
closed bool
mu sync.Mutex
}
### reset
因为连接是预先分配的,所以需要重置,相当于初始化
func (c *connection) reset(k key, s Stream, ts time.Time) {
c.key = k
c.pages = 0
c.first, c.last = nil, nil
c.nextSeq = invalidSequence
c.created = ts
c.stream = s
c.closed = false
}
### traverseConn
遍历双向链接page列表获取正确的放置给定序号的位置。
注意它是向后遍历的,从最大的序号开始,一直往下,因为我们假设常见的情况是TCP数据包的流是顺序,与最小损失或数据包重新排序。
遍历双向链接的page列表,以找到放置给定序号的正确位置。
注意,它是向后遍历的,从最高的序号开始,一直向下,因为我们假设通常的情况是,流的TCP包将按顺序出现,损失或包重排序最小。
func (c *connection) traverseConn(seq Sequence) (prev, current *page) {
prev = c.last
for prev != nil && prev.seq.Difference(seq) < 0 {
current = prev
prev = current.prev
}
return
}
### pushbettwen
首先插入双向链接列表first-…-last在另一个双链接列表中的节点prevnext之间。如果prev为nil,则首先成为连接列表中的新第一页。如果next为nil,则使last成为列表中新的最后一页。第一个/最后一个可能指向相同的页面。
func (c *connection) pushBetween(prev, next, first, last *page) {
// Maintain our doubly linked list
if next == nil || c.last == nil {
c.last = last
} else {
last.next = next
next.prev = last
}
if prev == nil || c.first == nil {
c.first = first
} else {
first.prev = prev
prev.next = first
}
}
## Reassembly
Reassembly由assembler传入stream中。
type Reassembly struct {
// TCP流的负载,可能为空
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
nil {
c.first = first
} else {
first.prev = prev
prev.next = first
}
}
## Reassembly
Reassembly由assembler传入stream中。
type Reassembly struct {
// TCP流的负载,可能为空
[外链图片转存中…(img-Uc2n28U0-1715633866735)]
[外链图片转存中…(img-wctPkAAM-1715633866735)]
[外链图片转存中…(img-zSEH6jWU-1715633866735)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新