golang network programming
1.TCP programming
Processing flow of TCP server program:
1.Listening port 2.Receive client request to establish link 3.establish goroutine Process links.
The TCP server code implemented using the net package of Go language is as follows:
Server
package main import ( "bufio" "fmt" "net" ) func process(conn net.Conn) { //Delay closing connection defer conn.Close() for { //Read conn //bufio.NewReader opens a file and returns a file handle reader := bufio.NewReader(conn) //Open a 128 byte character buffer var buf [128]byte //Read the contents of the reader and put them into buf. n is the size n, err := reader.Read(buf[:]) if err != nil { fmt.Println("Failed to read message from client..., err", err) break } else { fmt.Println("Received a piece of data:") } recvStr := string(buf[:n]) fmt.Println(recvStr) //Reply received successfully fmt.Println("Send a confirmation message to the client!") echo := "echo: " + recvStr conn.Write([]byte(echo)) } } func main() { //monitor //The server starts waiting for the client to connect, and the listen function will not block listen, err := net.Listen("tcp", "127.0.0.1:20000") if err != nil { fmt.Println("Failed to establish connection, err", err) return } fmt.Println("Ready to receive client connections...") for { //Establish connection //The accept function takes a connection from the established connection queue to the client program for point-to-point connection //The accept function returns a new available socket //The accept function blocks conn, err := listen.Accept() if err != nil { fmt.Println("connection failed, err", err) continue } else { fmt.Println("Connection succeeded!") } go process(conn) } }
client
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { //Start dialing conn, err := net.Dial("tcp", "127.0.0.1:20000") if err != nil { fmt.Println("err: ", err) return } defer conn.Close() //Open os.Stdin and return a file handle inputReader := bufio.NewReader(os.Stdin) for { //Read user input fmt.Println("Please enter the message to be sent:") //Input wrap end input input, _ := inputReader.ReadString('\n') //strings.Trim removes the matching content at the beginning and end of the string inputInfo := strings.Trim(input, "\r\n") //If you enter q, exit if strings.ToUpper(inputInfo) == "Q" { fmt.Println("Stop input and disconnect!") return } //send data fmt.Println("Start sending data...") _, err = conn.Write([]byte(inputInfo)) if err != nil { return } else { fmt.Println("Sending succeeded!") } buf := [512]byte{} n, err := conn.Read(buf[:]) if err != nil { fmt.Println("Receive failed, err: ", err) return } else { fmt.Println("Received a reply from the server:") } fmt.Println(string(buf[:n])) } }
2.UDP programming
The Chinese name of UDP protocol is User Datagram Protocol. It is a connectionless transport layer protocol in OSI (Open System Interconnection) reference model. It can directly send and receive data without establishing a connection. It belongs to unreliable and non sequential communication, but UDP protocol has good real-time performance, It is usually used in the field of live video broadcasting.
The UDP server code implemented using the net package of Go language is as follows
Server
package main import ( "fmt" "net" ) func main() { listen, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 30000, }) if err != nil { fmt.Println("Failed to establish monitoring!, err:", err) return } defer listen.Close() for { var data [1024]byte //receive data n, addr, err := listen.ReadFromUDP(data[:]) if err != nil { fmt.Println("Failed to receive data! err:", err) continue } fmt.Printf("data:%v addr:%v count:%v\n", string(data[:n]), addr, n) //send data _, err = listen.WriteToUDP(data[:n], addr) if err != nil { fmt.Println("Failed to send data! err:", err) continue } } }
client
package main import ( "fmt" "net" ) func main() { socket, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 30000, }) if err != nil { fmt.Println("Failed to connect to the server! err:", err) return } defer socket.Close() sendData := []byte("hello server") //send data _, err = socket.Write(sendData) if err != nil { fmt.Println("Failed to send data! err:", err) return } //receive data data := make([] byte, 4096) n, remoteAddr, err := socket.ReadFromUDP(data) if err != nil { fmt.Println("Failed to receive data! err:", err) return } fmt.Printf("recv:%v addr:%v count:%v\n\n", string(data[:n]), remoteAddr, n) }
3. TCP sticky packet
Sticky bag scene
The following code is a TCP server and client implemented with golang, which will generate sticky packets.
Server:
package main import ( "bufio" "fmt" "io" "net" ) func process(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) var buf [1024]byte for { n, err := reader.Read(buf[:]) if err == io.EOF { break } if err != nil { fmt.Println("read from client failed, err:", err) break } recvStr := string(buf[:n]) fmt.Println("received client Data sent:", recvStr) } } func main() { listen, err := net.Listen("tcp", "127.0.0.1:30000") if err != nil { fmt.Println("listen failed, err:", err) return } defer listen.Close() for { conn, err := listen.Accept() if err != nil { fmt.Println("accept failed, err:", err) continue } go process(conn) } }
client:
package main import ( "fmt" "net" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:30000") if err != nil { fmt.Println("dial failed, err", err) return } defer conn.Close() for i := 0; i < 20; i++ { msg := `Hello, Hello. How are you?` conn.Write([]byte(msg)) } }
Output results:
received client Data sent: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
The data sent by the client in 10 times is not successfully output 10 times at the server, but multiple pieces of data are "stuck" together.
Cause of sticking
The main reason is that tcp data transfer mode is stream mode, which can receive and send multiple times when maintaining a long connection.
"Sticky packet" can occur at the sending end or at the receiving end:
1. Sticky packets at the sender caused by Nagle algorithm: Nagle algorithm is an algorithm to improve network transmission efficiency. Simply put, 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.
2. Packet sticking at the receiving end caused by untimely reception at the receiving end: TCP will store the received data in its own buffer, and then notify the application layer to fetch the data. When the application layer can't take out the TCP data in time for some reasons, several segments of data will be stored in the TCP buffer.
terms of settlement
The key to "sticking packets" is that the receiver is uncertain about the size of the packets to be transmitted, so we can packet and unpack the packets.
Packet: packet is to add a packet header to a piece of data, so that the packet can be divided into two parts: packet header and packet body (the "packet tail" content will be added to the packet when filtering illegal packets). The length of the packet header is fixed, and it stores the length of the packet body. According to the fixed length of the packet header and the variable containing the length of the packet body in the packet header, a complete packet can be correctly split.
The commonly used protocol is TLV coding:
Next, we will implement simple LV coding, and T will ignore it for the time being.
The first four bytes of the packet are the packet header, which stores the length of the transmitted data.
Encoding and decoding
package LVEcode import ( "bufio" "bytes" "encoding/binary" ) // Encode encodes the message func Encode(message string) ([]byte, error) { //Read the length of the message and convert it into int32 type (4 bytes) var length = int32(len(message)) //Open a buffer var pkg = new(bytes.Buffer) //Write message header (write length to pkg) 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 } // Decode decode message func Decode(reader *bufio.Reader) (string, error) { //Read the length of the message //Read the first 4 bytes of data (int32 takes up 4 bytes) lengthByte, _ := reader.Peek(4) //Reads lengthByte and returns a handle lengthBuff := bytes.NewReader(lengthByte) var length int32 //Write the contents of lengthBuff to length err := binary.Read(lengthBuff, binary.LittleEndian, &length) if err != nil { return "", err } //Buffered returns the number of existing readable bytes in the buffer if int32(reader.Buffered()) < length + 4 { return "", err } //Read real message data pack := make([]byte, int(4 + length)) //Read the data in the reader into the pack slice _, err = reader.Read(pack) if err != nil { return "", err } //Returns a slice of valid data return string(pack[4:]), nil }
Server
package main import ( "NetWork/TCP3/LVEcode" "bufio" "fmt" "io" "net" ) func process(conn net.Conn) { defer conn.Close() //Open conn and return a handle reader := bufio.NewReader(conn) for { //Decode the received message msg, err := LVEcode.Decode(reader) if err == io.EOF { return } if err != nil { fmt.Println("decode msg failed, err:", err) return } fmt.Println("received client Data sent:", msg) } } func main() { listen, err := net.Listen("tcp", "127.0.0.1:30000") if err != nil { fmt.Println("listen failed, err:", err) return } defer listen.Close() for { conn, err := listen.Accept() if err != nil { fmt.Println("accept failed, err:", err) continue } go process(conn) } }
client
package main import ( "NetWork/TCP3/LVEcode" "fmt" "net" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:30000") if err != nil { fmt.Println("dial failed, err", err) return } defer conn.Close() for i := 0; i < 20; i++ { msg := `Hello, Hello. How are you?` //Encode the message to be sent first data, err := LVEcode.Encode(msg) if err != nil { fmt.Println("encode msg failed, err:", err) return } conn.Write(data) } }
Output:
received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you? received client Data sent: Hello, Hello. How are you?
It can be found that the package is no longer sticky.
4. HTTP programming
web Workflow
The working principle of Web server can be summarized as follows:
- The client establishes a TCP connection to the server through TCP/IP protocol
- The client sends an HTTP protocol request package to the server to request the resource document in the server
- The server sends an HTTP protocol response package to the client. If the requested resource contains dynamic language content, the server will call the interpretation engine of dynamic language to process the "dynamic content" and return the processed data to the client
- The client is disconnected from the server. The client interprets the HTML document and renders the graphic results on the client screen
HTTP protocol
- HyperText Transfer Protocol (HTTP)
Protocol is the most widely used network protocol on the Internet. It specifies the rules of communication between browser and World Wide Web server, and the data transmission protocol for transmitting World Wide Web documents through the Internet - The HTTP protocol is usually hosted on the TCP protocol
HTTP server
package main import ( "fmt" "net/http" ) func main() { //Register default route // /go is the request path //myHandler is a callback function written by itself http.HandleFunc("/go", myHandler) //addr: listening address //Handler: callback function http.ListenAndServe("127.0.0.1:8000", nil) } // Handler function func myHandler(w http.ResponseWriter, r *http.Request) { fmt.Println(r.RemoteAddr, "Connection succeeded") //Request method: GET POST DELETE PUT UPDATE fmt.Println("method:", r.Method) // /go fmt.Println("url:", r.URL.Path) fmt.Println("header:", r.Header) fmt.Println("body:", r.Body) //reply w.Write([]byte("www.51mh.com")) }
HTTP client
package main import ( "fmt" "io" "net/http" ) func main() { resp, _ := http.Get("http://127.0.0.1:8000/go") defer resp.Body.Close() //200 ok fmt.Println(resp.Status) fmt.Println(resp.Header) buf := make([]byte, 1024) for { //Receive server information n, err := resp.Body.Read(buf) if err != nil && err != io.EOF { fmt.Println(err) return } else { fmt.Println("Read complete") res := string(buf[:n]) fmt.Println(res) break } } }
Server running result:
127.0.0.1:8187 Connection succeeded method: GET url: /go header: map[Accept-Encoding:[gzip] User-Agent:[Go-http-client/1.1]] body: {}
Client run results:
200 OK map[Content-Length:[12] Content-Type:[text/plain; charset=utf-8] Date:[Thu, 28 Oct 2021 14:38:58 GMT]] Read complete www.51mh.com