mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-09 14:50:09 +08:00
fdbased 还是不太行
This commit is contained in:
@@ -147,7 +147,7 @@ func tcpListen(s *stack.Stack, proto tcpip.NetworkProtocolNumber, addr tcpip.Add
|
|||||||
|
|
||||||
// 绑定IP和端口,这里的IP地址为空,表示绑定任何IP
|
// 绑定IP和端口,这里的IP地址为空,表示绑定任何IP
|
||||||
// 此时就会调用端口管理器
|
// 此时就会调用端口管理器
|
||||||
if err := ep.Bind(tcpip.FullAddress{NIC: 1, Addr: "", Port: uint16(localPort)}, nil); err != nil {
|
if err := ep.Bind(tcpip.FullAddress{NIC: 1, Addr: addr, Port: uint16(localPort)}, nil); err != nil {
|
||||||
log.Fatal("Bind failed: ", err)
|
log.Fatal("Bind failed: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ func main() {
|
|||||||
log.Fatal("Usage: ", os.Args[0], " <tap-device> <local-address/mask> <ip-address> <local-port>")
|
log.Fatal("Usage: ", os.Args[0], " <tap-device> <local-address/mask> <ip-address> <local-port>")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.SetFlags(logger.HANDSHAKE)
|
logger.SetFlags(logger.ETH)
|
||||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||||
|
|
||||||
tapName := flag.Arg(0)
|
tapName := flag.Arg(0)
|
||||||
@@ -97,8 +97,6 @@ func main() {
|
|||||||
ResolutionRequired: true,
|
ResolutionRequired: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
_ = linkID
|
|
||||||
|
|
||||||
loopbackLinkID := loopback.New()
|
loopbackLinkID := loopback.New()
|
||||||
|
|
||||||
// 新建相关协议的协议栈
|
// 新建相关协议的协议栈
|
||||||
@@ -106,7 +104,13 @@ func main() {
|
|||||||
[]string{tcp.ProtocolName, udp.ProtocolName}, stack.Options{})
|
[]string{tcp.ProtocolName, udp.ProtocolName}, stack.Options{})
|
||||||
|
|
||||||
// 新建抽象的网卡
|
// 新建抽象的网卡
|
||||||
if err := s.CreateNamedNIC(1, "vnic1", loopbackLinkID); err != nil {
|
_ = loopbackLinkID
|
||||||
|
if err := s.CreateNamedNIC(1, "lo", loopbackLinkID); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = linkID
|
||||||
|
if err := s.CreateNamedNIC(2, "fdbase", linkID); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +213,6 @@ func TestServerEcho(conn *TcpConn) {
|
|||||||
conn.ep.Close()
|
conn.ep.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestServerCase1(conn *TcpConn) {
|
func TestServerCase1(conn *TcpConn) {
|
||||||
cnt := 0
|
cnt := 0
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
Binary file not shown.
378
note/README.md
378
note/README.md
@@ -8,380 +8,10 @@
|
|||||||
|
|
||||||
那么,有了这个认知,你可能会有种不太好的预感,是的,这是个巨大的工程。所幸谷歌为我们提供了一个极简版本的网络栈,在我跌跌撞撞地复现结束后,一个可以运行echo server的工程总共一万行出头,算是一个中等大小的项目了。
|
那么,有了这个认知,你可能会有种不太好的预感,是的,这是个巨大的工程。所幸谷歌为我们提供了一个极简版本的网络栈,在我跌跌撞撞地复现结束后,一个可以运行echo server的工程总共一万行出头,算是一个中等大小的项目了。
|
||||||
|
|
||||||
不多说,先看看效果
|
## 真实的内核如何接收网络包
|
||||||
|
|
||||||
## echo server
|
1. 当网卡收到数据以后,以DMA的方式把网卡收到的帧写到内存里,再向CPU发起一个中断,以通知CPU有数据到达。
|
||||||
|
2. 当CPU收到中断请求后,会去调用网络设备驱动注册的中断处理函数。网卡的中断处理函数并不做过多工作,发出软中断请求,然后尽快释放CPU资源。
|
||||||
``` go
|
3. ksoftirqd内核线程检测到有软中断请求到达,调用poll开始轮询收包,受到后交给各级协议栈处理。对于tcp包来说,会被放到用户socker的接受队列中。
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
if len(flag.Args()) != 4 {
|
|
||||||
log.Fatal("Usage: ", os.Args[0], " <tap-device> <local-address/mask> <ip-address> <local-port>")
|
|
||||||
}
|
|
||||||
|
|
||||||
tapName := flag.Arg(0)
|
|
||||||
cidrName := flag.Arg(1)
|
|
||||||
addrName := flag.Arg(2)
|
|
||||||
portName := flag.Arg(3)
|
|
||||||
|
|
||||||
// ... 解析各种配置
|
|
||||||
|
|
||||||
loopbackLinkID := loopback.New()
|
|
||||||
|
|
||||||
// 新建相关协议的协议栈
|
|
||||||
s := stack.New([]string{ipv4.ProtocolName, arp.ProtocolName},
|
|
||||||
[]string{tcp.ProtocolName, udp.ProtocolName}, stack.Options{})
|
|
||||||
|
|
||||||
// 新建抽象的网卡
|
|
||||||
if err := s.CreateNamedNIC(1, "vnic1", loopbackLinkID); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在该协议栈上添加和注册相应的网络层
|
|
||||||
if err := s.AddAddress(1, proto, addr); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在该协议栈上添加和注册ARP协议
|
|
||||||
if err := s.AddAddress(1, arp.ProtocolNumber, arp.ProtocolAddress); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加默认路由
|
|
||||||
s.SetRouteTable([]tcpip.Route{
|
|
||||||
{
|
|
||||||
Destination: tcpip.Address(strings.Repeat("\x00", len(addr))),
|
|
||||||
Mask: tcpip.AddressMask(strings.Repeat("\x00", len(addr))),
|
|
||||||
Gateway: "",
|
|
||||||
NIC: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
done := make(chan struct{}, 2)
|
|
||||||
|
|
||||||
go func() { // echo server
|
|
||||||
listener := tcpListen(s, proto, addr, localPort)
|
|
||||||
done <- struct{}{}
|
|
||||||
for {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
log.Println("服务端 建立连接")
|
|
||||||
|
|
||||||
go TestServerEcho(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-done
|
|
||||||
port := localPort
|
|
||||||
conn, err := Dial(s, header.IPv4ProtocolNumber, addr, port)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("客户端 建立连接\n\n客户端 写入数据\n")
|
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
conn.Write([]byte("Hello Netstack"))
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.NOTICE("客户端读取", string(buf[:n]))
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer close(done)
|
|
||||||
c := make(chan os.Signal)
|
|
||||||
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2)
|
|
||||||
<-c
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServerEcho(conn *TcpConn) {
|
|
||||||
for {
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
logger.NOTICE("服务端读取数据", string(buf[:n]))
|
|
||||||
conn.Write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.ep.Close()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
上面的代码看上去内容很多,其实并不复杂,在`done := make(chan struct{}, 2)`之前的代码是在初始化协议栈,类比的话相当于linux内核的启动,其中的内部细节并不需要用户程序了解,我们只需要关注内核对外暴露的接口即可。
|
|
||||||
|
|
||||||
随后的两个goroutine,一个是服务端,一个是客户端。
|
|
||||||
|
|
||||||
先看看服务端做了什么
|
|
||||||
|
|
||||||
``` go
|
|
||||||
go func() { // echo server
|
|
||||||
listener := tcpListen(s, proto, addr, localPort) // 监听端口
|
|
||||||
done <- struct{}{} // 通知客户端可以呼叫
|
|
||||||
for {
|
|
||||||
conn, err := listener.Accept() // 循环接受连接
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
log.Println("服务端 建立连接")
|
|
||||||
|
|
||||||
go TestServerEcho(conn) // 后台处理连接
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 连接的处理逻辑
|
|
||||||
func TestServerEcho(conn *TcpConn) {
|
|
||||||
for {
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := conn.Read(buf) // 从连接中读数据
|
|
||||||
if err != nil { // 当客户端关闭连接的时候 退出循环
|
|
||||||
log.Println(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
logger.NOTICE("服务端读取数据", string(buf[:n]))
|
|
||||||
conn.Write(buf) // 把读到的数据写回去
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.ep.Close() // 客户端关闭后 我们退出
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
非常简单的一个echo server 客户端写了什么 我们就回复什么
|
|
||||||
|
|
||||||
再来看看客户端
|
|
||||||
|
|
||||||
``` go
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-done // 等待服务端准备完毕
|
|
||||||
port := localPort
|
|
||||||
conn, err := Dial(s, header.IPv4ProtocolNumber, addr, port) // 呼叫服务端 建立连接
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 循环三次写入数据
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
conn.Write([]byte("Hello Netstack"))
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := conn.Read(buf) // 写完再读
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.NOTICE("客户端读取", string(buf[:n]))
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.Close() // 主动关闭连接
|
|
||||||
}()
|
|
||||||
```
|
|
||||||
|
|
||||||
我们来看看这个程序编译运行的结果
|
|
||||||
|
|
||||||
``` go
|
|
||||||
2022/12/29 14:02:15 main.go:44: tap: tap0, addr: 192.168.1.1, port: 9999
|
|
||||||
2022/12/29 14:02:15 ports.go:109: TCP 成功分配端口 9999
|
|
||||||
2022/12/29 14:02:15 connect.go:877: TCP STATE SENT
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 告诉对端 我的接收窗口为 65535
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [syn] 报文片段到 192.168.1.1:9999, seq: 1569505920, ack: 0, 可接收rcvWnd: 65535
|
|
||||||
2022/12/29 14:02:15 accept.go:370: TCP STATE LISTEN
|
|
||||||
2022/12/29 14:02:15 accept.go:228: 收到一个远端握手申请 SYN seq = 1569505920 客户端请携带 标记 iss 710316102 +1
|
|
||||||
2022/12/29 14:02:15 accept.go:217: 服务端握手成功 服务端的recver 9999
|
|
||||||
+------> 1048576 <-----+
|
|
||||||
| |
|
|
||||||
-----------------+-------------+-------------+------------------------+
|
|
||||||
| ANR 0 | not revived | rcvd unack | able rcv |
|
|
||||||
-----------------+-------------+-------------+------------------------+
|
|
||||||
^ ^ ^
|
|
||||||
| | |
|
|
||||||
1569505921 1569505921 1570554497
|
|
||||||
2022/12/29 14:02:15 accept.go:246: TCP STATE SYN_RCVD
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 告诉对端 我的接收窗口为 65535
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|syn] 报文片段到 192.168.1.1:26913, seq: 710316102, ack: 1569505921, 可接收rcvWnd: 65535
|
|
||||||
2022/12/29 14:02:15 connect.go:214: 客户端收到了 syn|ack segment
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505921, ack: 710316103, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 connect.go:901: 客户端握手成功 客户端的sender 26913
|
|
||||||
+-----> 65535 <------+
|
|
||||||
| Scale 5 |
|
|
||||||
-----------------+-------------+-------------+------------------
|
|
||||||
| 已确认 |UAC 0|NXT 1048576| 不可发送
|
|
||||||
-----------------+-------------+-------------+------------------
|
|
||||||
^ ^
|
|
||||||
| |
|
|
||||||
1569505921 1569505921
|
|
||||||
2022/12/29 14:02:15 main.go:163: 客户端 建立连接
|
|
||||||
2022/12/29 14:02:15 connect.go:332: TCP STATE ESTABLISHED
|
|
||||||
2022/12/29 14:02:15 main.go:148: 服务端 建立连接
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 扩张发送窗口到 1048576
|
|
||||||
|
|
||||||
客户端 写入数据
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 65535 最多发送数据 65483 缓存数据头 1569505921 缓存数据尾 1569505935 发送端缓存包数量 1 拥塞窗口为 10
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505921, ack: 710316103, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505921 为 1569505935 下一次发送的数据头为 1569505935
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710316103, ack: 1569505935, 可接收rcvWnd: 32767
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710316103 缓存数据尾 710317127 发送端缓存包数量 1 拥塞窗口为 10
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710316103, ack: 1569505935, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710316103 为 710317127 下一次发送的数据头为 710317127
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505935, ack: 710317127, 可接收rcvWnd: 32736
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 1569505935 缓存数据尾 1569505949 发送端缓存包数量 1 拥塞窗口为 11
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505935, ack: 710317127, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505935 为 1569505949 下一次发送的数据头为 1569505949
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710317127, ack: 1569505949, 可接收rcvWnd: 32767
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710317127 缓存数据尾 710318151 发送端缓存包数量 1 拥塞窗口为 11
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710317127, ack: 1569505949, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710317127 为 710318151 下一次发送的数据头为 710318151
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505949, ack: 710318151, 可接收rcvWnd: 32736
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 1569505949 缓存数据尾 1569505963 发送端缓存包数量 1 拥塞窗口为 12
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505949, ack: 710318151, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505949 为 1569505963 下一次发送的数据头为 1569505963
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710318151, ack: 1569505963, 可接收rcvWnd: 32767
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710318151 缓存数据尾 710319175 发送端缓存包数量 1 拥塞窗口为 12
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710318151, ack: 1569505963, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710318151 为 710319175 下一次发送的数据头为 710319175
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505963, ack: 710319175, 可接收rcvWnd: 32736
|
|
||||||
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
|
||||||
|
|
||||||
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|fin] 报文片段到 192.168.1.1:9999, seq: 1569505963, ack: 710319175, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505963 为 1569505964 下一次发送的数据头为 1569505964
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710319175, ack: 1569505964, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 main.go:202: endpoint is closed for receive
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|fin] 报文片段到 192.168.1.1:26913, seq: 710319175, ack: 1569505964, 可接收rcvWnd: 32768
|
|
||||||
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710319175 为 710319176 下一次发送的数据头为 710319176
|
|
||||||
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505964, ack: 710319176, 可接收rcvWnd: 32768
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
为了看得清楚一点,我将结果分了几个段,我们可以看到,在每个段的最后一行有 服务端/客户端 读取 Hello Netstack 数量也符合我们的程序逻辑
|
|
||||||
|
|
||||||
## 接口
|
|
||||||
|
|
||||||
在上面的程序中,我们可以发现和正常的网络编程不同,我使用了一些不同于go/net库的函数。
|
|
||||||
|
|
||||||
``` go
|
|
||||||
TcpListen()
|
|
||||||
|
|
||||||
Dial()
|
|
||||||
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
这些函数是我自己封装的,用于隐藏底层对协议栈的复杂操作。但这个封装是非常简陋的,仅仅是能测试而已,但我们先就用这个来简单说说。
|
|
||||||
|
|
||||||
首先是一个结构体,表示一条tcp的连接。
|
|
||||||
|
|
||||||
``` go
|
|
||||||
// TcpConn 一条tcp连接
|
|
||||||
type TcpConn struct {
|
|
||||||
raddr tcpip.FullAddress //
|
|
||||||
ep tcpip.Endpoint
|
|
||||||
wq *waiter.Queue
|
|
||||||
we *waiter.Entry
|
|
||||||
notifyCh chan struct{}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
对于服务端而言,它是这样建立的
|
|
||||||
|
|
||||||
``` go
|
|
||||||
func tcpListen(s *stack.Stack, proto tcpip.NetworkProtocolNumber, addr tcpip.Address, localPort int) *TcpConn {
|
|
||||||
var wq waiter.Queue
|
|
||||||
// 新建一个tcp端
|
|
||||||
ep, err := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绑定IP和端口,这里的IP地址为空,表示绑定任何IP
|
|
||||||
// 此时就会调用端口管理器
|
|
||||||
if err := ep.Bind(tcpip.FullAddress{NIC: 1, Addr: "", Port: uint16(localPort)}, nil); err != nil {
|
|
||||||
log.Fatal("Bind failed: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始监听
|
|
||||||
if err := ep.Listen(10); err != nil {
|
|
||||||
log.Fatal("Listen failed: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
|
||||||
return &TcpConn{
|
|
||||||
ep: ep,
|
|
||||||
wq: &wq,
|
|
||||||
we: &waitEntry,
|
|
||||||
notifyCh: notifyCh}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
对于客户端而言,它是这样建立的
|
|
||||||
|
|
||||||
``` go
|
|
||||||
// Dial 呼叫tcp服务端
|
|
||||||
func Dial(s *stack.Stack, proto tcpip.NetworkProtocolNumber, addr tcpip.Address, port int) (*TcpConn, error) {
|
|
||||||
remote := tcpip.FullAddress{
|
|
||||||
Addr: addr,
|
|
||||||
Port: uint16(port),
|
|
||||||
}
|
|
||||||
var wq waiter.Queue
|
|
||||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
|
||||||
wq.EventRegister(&waitEntry, waiter.EventOut)
|
|
||||||
defer wq.EventUnregister(&waitEntry)
|
|
||||||
// 新建一个tcp端
|
|
||||||
ep, err := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%s", err.String())
|
|
||||||
}
|
|
||||||
err = ep.Connect(remote)
|
|
||||||
if err != nil {
|
|
||||||
if err == tcpip.ErrConnectStarted {
|
|
||||||
<-notifyCh
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("%s", err.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ep.SetSockOpt(tcpip.KeepaliveEnabledOption(1))
|
|
||||||
ep.SetSockOpt(tcpip.KeepaliveIntervalOption(75 * time.Second))
|
|
||||||
ep.SetSockOpt(tcpip.KeepaliveIdleOption(30 * time.Second)) // 30s的探活心跳
|
|
||||||
ep.SetSockOpt(tcpip.KeepaliveCountOption(9))
|
|
||||||
|
|
||||||
return &TcpConn{
|
|
||||||
ep: ep,
|
|
||||||
wq: &wq,
|
|
||||||
we: &waitEntry,
|
|
||||||
notifyCh: notifyCh}, nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
425
note/echo/README.md
Normal file
425
note/echo/README.md
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
## echo server
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if len(flag.Args()) != 4 {
|
||||||
|
log.Fatal("Usage: ", os.Args[0], " <tap-device> <local-address/mask> <ip-address> <local-port>")
|
||||||
|
}
|
||||||
|
|
||||||
|
tapName := flag.Arg(0)
|
||||||
|
cidrName := flag.Arg(1)
|
||||||
|
addrName := flag.Arg(2)
|
||||||
|
portName := flag.Arg(3)
|
||||||
|
|
||||||
|
// ... 解析各种配置
|
||||||
|
|
||||||
|
loopbackLinkID := loopback.New()
|
||||||
|
|
||||||
|
// 新建相关协议的协议栈
|
||||||
|
s := stack.New([]string{ipv4.ProtocolName, arp.ProtocolName},
|
||||||
|
[]string{tcp.ProtocolName, udp.ProtocolName}, stack.Options{})
|
||||||
|
|
||||||
|
// 新建抽象的网卡
|
||||||
|
if err := s.CreateNamedNIC(1, "vnic1", loopbackLinkID); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在该协议栈上添加和注册相应的网络层
|
||||||
|
if err := s.AddAddress(1, proto, addr); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在该协议栈上添加和注册ARP协议
|
||||||
|
if err := s.AddAddress(1, arp.ProtocolNumber, arp.ProtocolAddress); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加默认路由
|
||||||
|
s.SetRouteTable([]tcpip.Route{
|
||||||
|
{
|
||||||
|
Destination: tcpip.Address(strings.Repeat("\x00", len(addr))),
|
||||||
|
Mask: tcpip.AddressMask(strings.Repeat("\x00", len(addr))),
|
||||||
|
Gateway: "",
|
||||||
|
NIC: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
done := make(chan struct{}, 2)
|
||||||
|
|
||||||
|
go func() { // echo server
|
||||||
|
listener := tcpListen(s, proto, addr, localPort)
|
||||||
|
done <- struct{}{}
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Println("服务端 建立连接")
|
||||||
|
|
||||||
|
go TestServerEcho(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-done
|
||||||
|
port := localPort
|
||||||
|
conn, err := Dial(s, header.IPv4ProtocolNumber, addr, port)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("客户端 建立连接\n\n客户端 写入数据\n")
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
conn.Write([]byte("Hello Netstack"))
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.NOTICE("客户端读取", string(buf[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer close(done)
|
||||||
|
c := make(chan os.Signal)
|
||||||
|
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2)
|
||||||
|
<-c
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerEcho(conn *TcpConn) {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
logger.NOTICE("服务端读取数据", string(buf[:n]))
|
||||||
|
conn.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.ep.Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
上面的代码看上去内容很多,其实并不复杂,在`done := make(chan struct{}, 2)`之前的代码是在初始化协议栈,类比的话相当于linux内核的启动,其中的内部细节并不需要用户程序了解,我们只需要关注内核对外暴露的接口即可。
|
||||||
|
|
||||||
|
随后的两个goroutine,一个是服务端,一个是客户端。
|
||||||
|
|
||||||
|
先看看服务端做了什么
|
||||||
|
|
||||||
|
``` go
|
||||||
|
go func() { // echo server
|
||||||
|
listener := tcpListen(s, proto, addr, localPort) // 监听端口
|
||||||
|
done <- struct{}{} // 通知客户端可以呼叫
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept() // 循环接受连接
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
log.Println("服务端 建立连接")
|
||||||
|
|
||||||
|
go TestServerEcho(conn) // 后台处理连接
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 连接的处理逻辑
|
||||||
|
func TestServerEcho(conn *TcpConn) {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf) // 从连接中读数据
|
||||||
|
if err != nil { // 当客户端关闭连接的时候 退出循环
|
||||||
|
log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
logger.NOTICE("服务端读取数据", string(buf[:n]))
|
||||||
|
conn.Write(buf) // 把读到的数据写回去
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.ep.Close() // 客户端关闭后 我们退出
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
非常简单的一个echo server 客户端写了什么 我们就回复什么
|
||||||
|
|
||||||
|
再来看看客户端
|
||||||
|
|
||||||
|
``` go
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-done // 等待服务端准备完毕
|
||||||
|
port := localPort
|
||||||
|
conn, err := Dial(s, header.IPv4ProtocolNumber, addr, port) // 呼叫服务端 建立连接
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环三次写入数据
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
conn.Write([]byte("Hello Netstack"))
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf) // 写完再读
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.NOTICE("客户端读取", string(buf[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Close() // 主动关闭连接
|
||||||
|
}()
|
||||||
|
```
|
||||||
|
|
||||||
|
我们来看看这个程序编译运行的结果
|
||||||
|
|
||||||
|
``` go
|
||||||
|
2022/12/29 14:02:15 main.go:44: tap: tap0, addr: 192.168.1.1, port: 9999
|
||||||
|
2022/12/29 14:02:15 ports.go:109: TCP 成功分配端口 9999
|
||||||
|
2022/12/29 14:02:15 connect.go:877: TCP STATE SENT
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 告诉对端 我的接收窗口为 65535
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [syn] 报文片段到 192.168.1.1:9999, seq: 1569505920, ack: 0, 可接收rcvWnd: 65535
|
||||||
|
2022/12/29 14:02:15 accept.go:370: TCP STATE LISTEN
|
||||||
|
2022/12/29 14:02:15 accept.go:228: 收到一个远端握手申请 SYN seq = 1569505920 客户端请携带 标记 iss 710316102 +1
|
||||||
|
2022/12/29 14:02:15 accept.go:217: 服务端握手成功 服务端的recver 9999
|
||||||
|
+------> 1048576 <-----+
|
||||||
|
| |
|
||||||
|
-----------------+-------------+-------------+------------------------+
|
||||||
|
| ANR 0 | not revived | rcvd unack | able rcv |
|
||||||
|
-----------------+-------------+-------------+------------------------+
|
||||||
|
^ ^ ^
|
||||||
|
| | |
|
||||||
|
1569505921 1569505921 1570554497
|
||||||
|
2022/12/29 14:02:15 accept.go:246: TCP STATE SYN_RCVD
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 告诉对端 我的接收窗口为 65535
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|syn] 报文片段到 192.168.1.1:26913, seq: 710316102, ack: 1569505921, 可接收rcvWnd: 65535
|
||||||
|
2022/12/29 14:02:15 connect.go:214: 客户端收到了 syn|ack segment
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505921, ack: 710316103, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 connect.go:901: 客户端握手成功 客户端的sender 26913
|
||||||
|
+-----> 65535 <------+
|
||||||
|
| Scale 5 |
|
||||||
|
-----------------+-------------+-------------+------------------
|
||||||
|
| 已确认 |UAC 0|NXT 1048576| 不可发送
|
||||||
|
-----------------+-------------+-------------+------------------
|
||||||
|
^ ^
|
||||||
|
| |
|
||||||
|
1569505921 1569505921
|
||||||
|
2022/12/29 14:02:15 main.go:163: 客户端 建立连接
|
||||||
|
2022/12/29 14:02:15 connect.go:332: TCP STATE ESTABLISHED
|
||||||
|
2022/12/29 14:02:15 main.go:148: 服务端 建立连接
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 扩张发送窗口到 1048576
|
||||||
|
|
||||||
|
客户端 写入数据
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 65535 最多发送数据 65483 缓存数据头 1569505921 缓存数据尾 1569505935 发送端缓存包数量 1 拥塞窗口为 10
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505921, ack: 710316103, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505921 为 1569505935 下一次发送的数据头为 1569505935
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710316103, ack: 1569505935, 可接收rcvWnd: 32767
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710316103 缓存数据尾 710317127 发送端缓存包数量 1 拥塞窗口为 10
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710316103, ack: 1569505935, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710316103 为 710317127 下一次发送的数据头为 710317127
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505935, ack: 710317127, 可接收rcvWnd: 32736
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 1569505935 缓存数据尾 1569505949 发送端缓存包数量 1 拥塞窗口为 11
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505935, ack: 710317127, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505935 为 1569505949 下一次发送的数据头为 1569505949
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710317127, ack: 1569505949, 可接收rcvWnd: 32767
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710317127 缓存数据尾 710318151 发送端缓存包数量 1 拥塞窗口为 11
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710317127, ack: 1569505949, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710317127 为 710318151 下一次发送的数据头为 710318151
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505949, ack: 710318151, 可接收rcvWnd: 32736
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 1569505949 缓存数据尾 1569505963 发送端缓存包数量 1 拥塞窗口为 12
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|psh] 报文片段到 192.168.1.1:9999, seq: 1569505949, ack: 710318151, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505949 为 1569505963 下一次发送的数据头为 1569505963
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710318151, ack: 1569505963, 可接收rcvWnd: 32767
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 服务端读取数据 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 snd.go:586: 发送窗口是 1048576 最多发送数据 65483 缓存数据头 710318151 缓存数据尾 710319175 发送端缓存包数量 1 拥塞窗口为 12
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|psh] 报文片段到 192.168.1.1:26913, seq: 710318151, ack: 1569505963, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710318151 为 710319175 下一次发送的数据头为 710319175
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505963, ack: 710319175, 可接收rcvWnd: 32736
|
||||||
|
2022/12/29 14:02:15 logger.go:75: NOTICE: 客户端读取 Hello Netstack
|
||||||
|
|
||||||
|
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack|fin] 报文片段到 192.168.1.1:9999, seq: 1569505963, ack: 710319175, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 26913 更新sndNxt 1569505963 为 1569505964 下一次发送的数据头为 1569505964
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack] 报文片段到 192.168.1.1:26913, seq: 710319175, ack: 1569505964, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 main.go:202: endpoint is closed for receive
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :9999 发送 [ack|fin] 报文片段到 192.168.1.1:26913, seq: 710319175, ack: 1569505964, 可接收rcvWnd: 32768
|
||||||
|
2022/12/29 14:02:15 snd.go:603: 9999 更新sndNxt 710319175 为 710319176 下一次发送的数据头为 710319176
|
||||||
|
2022/12/29 14:02:15 connect.go:608: TCP :26913 发送 [ack] 报文片段到 192.168.1.1:9999, seq: 1569505964, ack: 710319176, 可接收rcvWnd: 32768
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
为了看得清楚一点,我将结果分了几个段,我们可以看到,在每个段的最后一行有 服务端/客户端 读取 Hello Netstack 数量也符合我们的程序逻辑
|
||||||
|
|
||||||
|
## 接口
|
||||||
|
|
||||||
|
在上面的程序中,我们可以发现和正常的网络编程不同,我使用了一些不同于go/net库的函数。
|
||||||
|
|
||||||
|
``` go
|
||||||
|
TcpListen()
|
||||||
|
|
||||||
|
Dial()
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
这些函数是我自己封装的,用于隐藏底层对协议栈的复杂操作。但这个封装是非常简陋的,仅仅是能测试而已,但我们先就用这个来简单说说。
|
||||||
|
|
||||||
|
首先是一个结构体,表示一条tcp的连接。
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// TcpConn 一条tcp连接
|
||||||
|
type TcpConn struct {
|
||||||
|
raddr tcpip.FullAddress
|
||||||
|
ep tcpip.Endpoint
|
||||||
|
wq *waiter.Queue
|
||||||
|
we *waiter.Entry
|
||||||
|
notifyCh chan struct{}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
对于服务端而言,它是这样建立的
|
||||||
|
|
||||||
|
``` go
|
||||||
|
func tcpListen(s *stack.Stack, proto tcpip.NetworkProtocolNumber, addr tcpip.Address, localPort int) *TcpConn {
|
||||||
|
var wq waiter.Queue
|
||||||
|
// 新建一个tcp端
|
||||||
|
ep, err := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定IP和端口,这里的IP地址为空,表示绑定任何IP
|
||||||
|
// 此时就会调用端口管理器
|
||||||
|
if err := ep.Bind(tcpip.FullAddress{NIC: 1, Addr: "", Port: uint16(localPort)}, nil); err != nil {
|
||||||
|
log.Fatal("Bind failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始监听
|
||||||
|
if err := ep.Listen(10); err != nil {
|
||||||
|
log.Fatal("Listen failed: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||||
|
return &TcpConn{
|
||||||
|
ep: ep,
|
||||||
|
wq: &wq,
|
||||||
|
we: &waitEntry,
|
||||||
|
notifyCh: notifyCh}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
对于客户端而言,它是这样建立的
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// Dial 呼叫tcp服务端
|
||||||
|
func Dial(s *stack.Stack, proto tcpip.NetworkProtocolNumber, addr tcpip.Address, port int) (*TcpConn, error) {
|
||||||
|
remote := tcpip.FullAddress{
|
||||||
|
Addr: addr,
|
||||||
|
Port: uint16(port),
|
||||||
|
}
|
||||||
|
var wq waiter.Queue
|
||||||
|
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||||
|
wq.EventRegister(&waitEntry, waiter.EventOut)
|
||||||
|
defer wq.EventUnregister(&waitEntry)
|
||||||
|
// 新建一个tcp端
|
||||||
|
ep, err := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s", err.String())
|
||||||
|
}
|
||||||
|
err = ep.Connect(remote)
|
||||||
|
if err != nil {
|
||||||
|
if err == tcpip.ErrConnectStarted {
|
||||||
|
<-notifyCh
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("%s", err.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ep.SetSockOpt(tcpip.KeepaliveEnabledOption(1))
|
||||||
|
ep.SetSockOpt(tcpip.KeepaliveIntervalOption(75 * time.Second))
|
||||||
|
ep.SetSockOpt(tcpip.KeepaliveIdleOption(30 * time.Second)) // 30s的探活心跳
|
||||||
|
ep.SetSockOpt(tcpip.KeepaliveCountOption(9))
|
||||||
|
|
||||||
|
return &TcpConn{
|
||||||
|
ep: ep,
|
||||||
|
wq: &wq,
|
||||||
|
we: &waitEntry,
|
||||||
|
notifyCh: notifyCh}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
数据的读写
|
||||||
|
|
||||||
|
``` go
|
||||||
|
// Read 读数据
|
||||||
|
func (conn *TcpConn) Read(rcv []byte) (int, error) {
|
||||||
|
conn.wq.EventRegister(conn.we, waiter.EventIn)
|
||||||
|
defer conn.wq.EventUnregister(conn.we)
|
||||||
|
for {
|
||||||
|
buf, _, err := conn.ep.Read(&conn.raddr)
|
||||||
|
if err != nil {
|
||||||
|
if err == tcpip.ErrWouldBlock {
|
||||||
|
<-conn.notifyCh
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%s", err.String())
|
||||||
|
}
|
||||||
|
n := len(buf)
|
||||||
|
if n > cap(rcv) {
|
||||||
|
n = cap(rcv)
|
||||||
|
}
|
||||||
|
rcv = append(rcv[:0], buf[:n]...)
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 写数据
|
||||||
|
func (conn *TcpConn) Write(snd []byte) error {
|
||||||
|
conn.wq.EventRegister(conn.we, waiter.EventOut)
|
||||||
|
defer conn.wq.EventUnregister(conn.we)
|
||||||
|
for {
|
||||||
|
n, _, err := conn.ep.Write(tcpip.SlicePayload(snd), tcpip.WriteOptions{To: &conn.raddr})
|
||||||
|
if err != nil {
|
||||||
|
if err == tcpip.ErrWouldBlock {
|
||||||
|
<-conn.notifyCh
|
||||||
|
if int(n) < len(snd) && n > 0 {
|
||||||
|
snd = snd[n:]
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s", err.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close 关闭连接
|
||||||
|
func (conn *TcpConn) Close() {
|
||||||
|
conn.ep.Close()
|
||||||
|
}
|
||||||
|
```
|
@@ -1,2 +1,3 @@
|
|||||||
# 链路层
|
# 链路层
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package fdbased
|
package fdbased
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"netstack/logger"
|
"netstack/logger"
|
||||||
"netstack/tcpip"
|
"netstack/tcpip"
|
||||||
@@ -128,7 +129,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
|
|||||||
}
|
}
|
||||||
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
|
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
|
||||||
logger.GetInstance().Info(logger.ETH, func() {
|
logger.GetInstance().Info(logger.ETH, func() {
|
||||||
log.Println("链路层写回以太报文")
|
log.Println("链路层写回以太报文 ", r.LocalAddress, " to ", r.RemoteAddress)
|
||||||
})
|
})
|
||||||
// 写入网卡中
|
// 写入网卡中
|
||||||
if payload.Size() == 0 {
|
if payload.Size() == 0 {
|
||||||
|
@@ -44,10 +44,6 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
|
|||||||
views = append(views, payload.Views()...)
|
views = append(views, payload.Views()...)
|
||||||
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
|
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
|
||||||
|
|
||||||
// TODO 这里整点活 在特定的情况下丢掉数据报 模拟网络阻塞
|
|
||||||
//rand.Seed(time.Now().Unix())
|
|
||||||
//time.Sleep(time.Duration(rand.Intn(50)+50) * time.Millisecond)
|
|
||||||
|
|
||||||
e.count++
|
e.count++
|
||||||
//if e.count == 6 { // 丢掉客户端写入的第二个包
|
//if e.count == 6 { // 丢掉客户端写入的第二个包
|
||||||
// logger.NOTICE(fmt.Sprintf("统计 %d 丢掉这个报文", e.count))
|
// logger.NOTICE(fmt.Sprintf("统计 %d 丢掉这个报文", e.count))
|
||||||
|
@@ -111,6 +111,7 @@ func (r *Route) RemoveWaker(waker *sleep.Waker) {
|
|||||||
|
|
||||||
// IsResolutionRequired returns true if Resolve() must be called to resolve
|
// IsResolutionRequired returns true if Resolve() must be called to resolve
|
||||||
// the link address before the this route can be written to.
|
// the link address before the this route can be written to.
|
||||||
|
// 本地缓存有东西 而且不知道远端的MAC
|
||||||
func (r *Route) IsResolutionRequired() bool {
|
func (r *Route) IsResolutionRequired() bool {
|
||||||
return r.ref.linkCache != nil && r.RemoteLinkAddress == ""
|
return r.ref.linkCache != nil && r.RemoteLinkAddress == ""
|
||||||
}
|
}
|
||||||
|
@@ -150,7 +150,7 @@ func (h *handshake) resetToSynRcvd(iss seqnum.Value, irs seqnum.Value, opts *hea
|
|||||||
h.sndWndScale = opts.WS
|
h.sndWndScale = opts.WS
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 处理tcp路由
|
// 处理tcp路由
|
||||||
func (h *handshake) resolveRoute() *tcpip.Error {
|
func (h *handshake) resolveRoute() *tcpip.Error {
|
||||||
// Set up the wakers.
|
// Set up the wakers.
|
||||||
s := sleep.Sleeper{}
|
s := sleep.Sleeper{}
|
||||||
@@ -166,7 +166,6 @@ func (h *handshake) resolveRoute() *tcpip.Error {
|
|||||||
case wakerForResolution:
|
case wakerForResolution:
|
||||||
if _, err := h.ep.route.Resolve(resolutionWaker); err != tcpip.ErrWouldBlock {
|
if _, err := h.ep.route.Resolve(resolutionWaker); err != tcpip.ErrWouldBlock {
|
||||||
// Either success (err == nil) or failure.
|
// Either success (err == nil) or failure.
|
||||||
log.Println("没有地址", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Resolution not completed. Keep trying...
|
// Resolution not completed. Keep trying...
|
||||||
@@ -406,6 +405,7 @@ func (h *handshake) execute() *tcpip.Error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the resend timer.
|
// Initialize the resend timer.
|
||||||
// 初始化重传定时器
|
// 初始化重传定时器
|
||||||
resendWaker := sleep.Waker{}
|
resendWaker := sleep.Waker{}
|
||||||
|
Reference in New Issue
Block a user