preface
This article is not about http Net package, which only introduces the traditional network communication (the http.net package will be updated separately later)
1, Hierarchy of the Internet
It is roughly divided into four layers. If subdivided, it can be divided into seven layers
1. Application layer
Application layer, presentation layer and session layer
2. Transport layer
Transport layer
3. Network layer
network layer
4. Network interface layer
Data link layer, physical layer
5. Illustration
among socket The first floor is for us,We can implement some functions in this layer to meet the needs of users
2, tcp protocol overview
1. Three handshakes
First handshake
Client sends a TCP of SYN The packet at flag position 1 indicates the port of the server to which the customer intends to connect, And initial serial number X,Serial number saved in the header(Sequence Number)Field.
The Second Handshake
The server sends back a confirmation packet(ACK)answer. Namely SYN Flag bit sum ACK All flag bits are 1 When, the serial number will be confirmed(Acknowledgement Number)Set as customer's I S N Add 1 to.Namely X+1.
Third handshake
The client sends the confirmation packet again(ACK) SYN The flag bit is 0,ACK The flag bit is 1.And put the clothes From the server ACK Serial number field for+1,Put it in the OK field and send it to the other party.And write in the data segment ISN of+1
2. What is TCP usually used for?
- Due to the precise transmission of tcp packets, it can be used to transmit important data such as files, pictures and so on
- tcp has high transmission accuracy but low efficiency. It is not easy to transmit data with large amount and low importance
3. The code realizes tcp communication
The client code is as follows:
package main import ( "bufio" "fmt" "net" "os" "strings" "time" ) // tcp/client/main.go // receive data func getMsg(conn net.Conn) { for { buf := [512]byte{} // n is the obtained data length n, err := conn.Read(buf[:]) if err != nil { fmt.Println("recv failed, err:", err) return } fmt.Println(string(buf[:n])) } } // send data func sendMsg(conn net.Conn) { for { // Create a buffer reader inputReader := bufio.NewReader(os.Stdin) // Receive user input with \ n as Terminator input, _ := inputReader.ReadString('\n') // Read user input // Remove the acquired \ n inputInfo := strings.Trim(input, "\r\n") // Judge whether the exit conditions are met if strings.ToUpper(inputInfo) == "Q" { // Send exit request to server _, _ = conn.Write([]byte("q")) return } // Write data to the server (data is transmitted in bytes between the two ends) _, err := conn.Write([]byte(inputInfo)) if err != nil { return } } } // client func main() { // Use tcp to connect 127.0.0.1:20000 conn, err := net.Dial("tcp", "127.0.0.1:20000") if err != nil { fmt.Println("err :", err) return } // Close the connection when the function reaches the end defer conn.Close() go getMsg(conn) go sendMsg(conn) time.Sleep(time.Hour) }
The server code is as follows:
package main import ( "bufio" "fmt" "net" "os" "strings" ) // TCP server side func sendMsg(conn net.Conn) { for { inputer := bufio.NewReader(os.Stdin) input, _ := inputer.ReadString('\n') input = strings.Trim(input, "\r\n") conn.Write([]byte(input)) } } // Receive processing function func process(conn net.Conn) { fmt.Println("line:", conn, "Connection succeeded!") defer conn.Close() // Close connection for { reader := bufio.NewReader(conn) var buf [128]byte n, err := reader.Read(buf[:]) // Read data if err != nil { fmt.Println("read from client failed, err:", err) break } recvStr := string(buf[:n]) if recvStr == "q" { fmt.Println(conn, "Disconnected!") return } fmt.Println(conn, ":", recvStr) fmt.Printf("Please enter:") } } func main() { // Bind 127.0.0.1:20000 of the gateway for data transmission in the form of tcp listen, err := net.Listen("tcp", "127.0.0.1:20000") if err != nil { fmt.Println("listen failed, err:", err) return } for { // Waiting for client to connect conn, err := listen.Accept() // Establish connection if err != nil { fmt.Println("accept failed, err:", err) continue } // Add the accepted connection to the daemon go process(conn) go sendMsg(conn) } }
3, udp protocol overview
1. What is UDP usually used for?
- udp has high transmission efficiency, but it is easy to lose data
- It can be used for webcast and video call
- Keep important information and try to keep unnecessary information
2. Code implementation
The client code is as follows:
package main import ( "bufio" "fmt" "net" "os" ) // UDP client func main() { socket, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 40000, }) if err != nil { fmt.Println("Failed to connect to the server, err:", err) return } defer socket.Close() var reply [1024]byte reader := bufio.NewReader(os.Stdin) for { fmt.Print("Please enter:") msg, _ := reader.ReadString('\n') socket.Write([]byte(msg)) // Received data n, _, err := socket.ReadFromUDP(reply[:]) if err != nil { fmt.Println("redv reply msg failed,err:", err) return } fmt.Println("Reply received:", string(reply[:n])) } }
The server code is as follows:
package main import ( "fmt" "net" "strings" ) // UDP server func main() { // There is no need to use the accept method to establish a UDP server conn, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 40000, }) if err != nil { fmt.Println("listen UDP failed,err:", err) return } defer conn.Close() // There is no need to establish a connection and send and receive data directly var data [1024]byte for { n, addr, err := conn.ReadFromUDP(data[:]) if err != nil { fmt.Println("read from UDP failed,err:", err) return } fmt.Println(data[:n]) reply := strings.ToUpper(string(data[:n])) // send data conn.WriteToUDP([]byte(reply), addr) } }
4, Sticking package problem [and solutions]
1. Why does it stick?
- Due to the frequent sending and receiving of data, in order to improve the sending and receiving efficiency, the bottom layer will check whether there are other data to be sent before sending messages
- If it is sent too frequently, the last packet will stick to the next datagram in summer, resulting in very chaotic data
- Sticky packets at the sender caused by Nagle algorithm: Nagle algorithm is an algorithm to improve network transmission efficiency. In short, when we submit a piece of data to TCP for transmission, TCP does not immediately send this piece of data, but waits for a short period of time to see if there is still data to be sent during the waiting period. If so, it will send these two pieces of data at one time
/* Receive 20 tomhellos sent by the client at the server and print them Printing effect under sticky package (various information may be directly connected) hello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tomhello Tom -------------- hello Tomhello Tomhello Tom -------------- The normal print result is: -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- hello Tomhello -------------- ...... */
2. Solutions
- Save the size of each data packet, write a header of the data packet to store the size of the data packet, and read the data first each time
- Read the packet size and read the data information entity
- Reduce the frequency of sending data
3. Encoding and decoding function
The encoding and decoding function code is as follows:
The client code is as follows:package edcode import ( "bufio" "bytes" "encoding/binary" ) /* Small end low low: low address memory low Big end high and low: high site storage low Encode and decode the information to be sent (packaging processing) The first four bytes in the packet are used to store the size of the packet, and the following bytes are used to store the main content of the information */ // decode // Take out the first four bytes of bytes, convert them to int32 type, and then take out the remaining message body func Mydecode(reader *bufio.Reader) (string, error) { Size, _ := reader.Peek(4) lenthbuff := bytes.NewBuffer(Size) var lenth int32 // Read the contents in lenthbuff. After reading, put the data in lenth. The reading method is small end err := binary.Read(lenthbuff, binary.LittleEndian, &lenth) // Judge whether there is an error. If the length is not enough, throw an exception if err != nil || int32(reader.Buffered()) < lenth+4 { return "", err } // pack := make([]byte, int(lenth+4)) // After read, there is less data in the buffer _, err = reader.Read(pack) if err != nil { return "", err } return string(pack[4:]), nil } // code // First calculate the length of the string, and then package it func MyEncode(message string) ([]byte, error) { // Read the length of the message and convert it to int32 type (4 bytes) var length = int32(len(message)) var pkg = new(bytes.Buffer) // Write header err := binary.Write(pkg, binary.LittleEndian, length) if err != nil { return nil, err } // Write message entity err = binary.Write(pkg, binary.LittleEndian, []byte(message)) if err != nil { return nil, err } return pkg.Bytes(), nil }
package main import ( "fmt" "net" ed "aCorePackage/edcode" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:20000") if err != nil { fmt.Println(err) } // Fast sending information will still stick together, but the information can be well separated through certain encoding and decoding methods for i := 0; i < 20; i++ { str := "hello Tom" mybtys, err := ed.MyEncode(str) if err != nil { fmt.Println(err) } conn.Write(mybtys) fmt.Println("-------------------") } }
The server code is as follows:
package main import ( ed "aCorePackage/edcode" "bufio" "fmt" "io" "net" ) func readMsd(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) for { str, err := ed.Mydecode(reader) if err == io.EOF { return } if err != nil { fmt.Println(err) return } fmt.Println(str) fmt.Println("--------------") } } func main() { listener, err := net.Listen("tcp", "127.0.0.1:20000") if err != nil { fmt.Println(err) } for { conn, err := listener.Accept() if err != nil { fmt.Println(err) return } go readMsd(conn) } }