DEV Community

Masui Masanori
Masui Masanori

Posted on

[Go] Try TCP 1

#go

Intro

In this time, I will try sending and receiving data using TCP.
Also, I will try watching the packets at that time using Wireshark.

Environments

  • Go ver.1.21.0
  • Wireshark ver.4.0.8

Sample projects

[Client] main.go

package main

import (
    "fmt"
    "log"
    "net"
)

func main() {
    var conn net.Conn
    var err error
    defer func() {
        if conn != nil {
            conn.Close()
        }
    }()
    buf := make([]byte, 1024)
    conn, err = net.Dial("tcp", ":5055")
    if err != nil {
        log.Panicln(err.Error())
    }
    data := []byte(fmt.Sprintln("Hello1"))
    _, err = conn.Write(data)
    if err != nil {
        log.Panicln(err.Error())
    }
    leng, err := conn.Read(buf)
    if err != nil {
        log.Panicln(err.Error())
    }
    log.Println(string(buf[:leng]))
}

Enter fullscreen mode Exit fullscreen mode

[Server] main.go

package main

import (
    "fmt"
    "log"
    "net"
)

func main() {
    socket, err := net.Listen("tcp", "localhost:5055")
    if err != nil {
        log.Fatal(err.Error())
    }
    defer socket.Close()
    conn, err := socket.Accept()
    if err != nil {
        log.Fatal(err.Error())
    }
    defer conn.Close()
    for {
        buf := make([]byte, 1024)
        length, err := conn.Read(buf)
        if err != nil {
            log.Fatal(err.Error())
        }
        result := ""
        for i := 0; i < length; i++ {
            result = fmt.Sprintf("%s %02X", result, buf[i])
        }
        log.Println(result)
        log.Println(string(buf[:length]))
        _, err = conn.Write([]byte(fmt.Sprintf("Received: %d", length)))
        if err != nil {
            log.Fatal(err.Error())
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

[Client] Result

2023/09/13 01:26:23 Received: 7
Enter fullscreen mode Exit fullscreen mode

[Server] Results

2023/09/13 01:26:23  48 65 6C 6C 6F 31 0A
2023/09/13 01:26:23 Hello1

2023/09/13 01:26:24 EOF
exit status 1
Enter fullscreen mode Exit fullscreen mode

Watching packets

I will try following the flow of the above processing from the packets captured with Wireshark.

Connecting

Image description

  1. First, establish a TCP connection using 3-way handshake. At that time, each application generates a Sequence Number and sends it to the other party with the SYN or ACK flags.
  2. After receiving the number, they add 1 to it and hold it as an Acknowledgment Number. The value is the number that the application expects to be sent as the next Sequence Number.
  3. Now, both have the same values.

Send text data

Image description

  1. The client sends text data(7 bytes) to the server.
  2. The server receives them and adds 7 to the last Acknowledgment Number and hold it as new one.
  3. Now, both have the same values.

How to get the received data length?

To get new Acknowledge Number, the receiver has to get the received data length.
But according to RFC9293, the TCP header doesn't have data length.
It only has TCP header length(== Data Offset).

According to RFC791, the IP header has total length and IP header length.

Total Length(47) - (IP header length(5 * 4) + TCP header length(5 * 4)) = 7(bytes)  
Enter fullscreen mode Exit fullscreen mode

Sending data repeatedly in a short period of time

When I try sending data repeatedly in a short period of time, all of the received data will be merged.

[Client] main.go

...
func main() {
    var conn net.Conn
    var err error
    defer func() {
        if conn != nil {
            conn.Close()
        }
    }()
    buf := make([]byte, 1024)
    conn, err = net.Dial("tcp", ":5055")
    if err != nil {
        log.Panicln(err.Error())
    }
    data := []byte(fmt.Sprintln("Hello1"))
    _, err = conn.Write(data)
    if err != nil {
        log.Panicln(err.Error())
    }
    // second
    data = []byte(fmt.Sprintln("Hello2"))
    _, err = conn.Write(data)
    if err != nil {
        log.Panicln(err.Error())
    }
    // Third
    data = []byte(fmt.Sprintln("Hello3"))
    _, err = conn.Write(data)
    if err != nil {
        log.Panicln(err.Error())
    }
    leng, err := conn.Read(buf)
    if err != nil {
        log.Panicln(err.Error())
    }
    log.Println(string(buf[:leng]))
}
Enter fullscreen mode Exit fullscreen mode

[Client] Result

2023/09/13 01:26:23 Received: 21
Enter fullscreen mode Exit fullscreen mode

[Server] Results

2023/09/14 02:01:25  48 65 6C 6C 6F 31 0A 48 65 6C 6C 6F 32 0A 48 65 6C 6C 6F 33 0A
2023/09/14 02:01:25 Hello1
Hello2
Hello3

2023/09/14 02:01:25 EOF
exit status 1
Enter fullscreen mode Exit fullscreen mode

Image description

To avoid that, I can change the buffer size to 7, because all of the data have same length.

[Server] main.go

...
func main() {
...
    defer conn.Close()
    for {
        buf := make([]byte, 7)
        length, err := conn.Read(buf)
        if err != nil {
            log.Fatal(err.Error())
        }
        result := ""
        for i := 0; i < length; i++ {
            result = fmt.Sprintf("%s %02X", result, buf[i])
        }
        log.Println(result)
        log.Println(string(buf[:length]))
        _, err = conn.Write([]byte(fmt.Sprintf("Received: %d", length)))
        if err != nil {
            log.Fatal(err.Error())
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

[Client] Result

2023/09/13 01:26:23 Received: 7
Enter fullscreen mode Exit fullscreen mode

[Server] Results

2023/09/14 02:10:50  48 65 6C 6C 6F 31 0A
2023/09/14 02:10:50 Hello1

2023/09/14 02:10:50  48 65 6C 6C 6F 32 0A
2023/09/14 02:10:50 Hello2

2023/09/14 02:10:50  48 65 6C 6C 6F 33 0A
2023/09/14 02:10:50 Hello3

2023/09/14 02:10:50 read tcp 127.0.0.1:5055->127.0.0.1:52505: wsarecv: An existing connection was forcibly closed by the remote host.
exit status 1
Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (0)