Go---Go network programming (detailed)

The core of the Internet is a series of protocols, collectively known as "Internet Protocol Suite". It is these protocols that specify how computers connect and network. When we understand these protocols, we understand the principle of the Internet. Because these protocols are too large and complex, there is no way to generalize them here. We can only introduce a few protocols we have more contact with in our daily development.

Internet layered model

The logical implementation of the Internet is divided into several layers. Each floor has its own function. Just like a building, each floor is supported by the next floor. Users only touch the top layer, and they don't feel the lower layers at all. To understand the Internet, we need to understand the functions of each layer from bottom to top.

As shown in the figure above, the Internet will have different layers according to different models, but no matter what model is divided according to, the higher the layer is closer to the user, and the lower the layer is closer to the hardware. In software development, we most use the model that divides the Internet into five layers in the figure above.

physical layer

To communicate with the outside world through the Internet, our computer needs to be connected to the network first. We can use twisted pair, optical fiber, radio wave and so on. This is called "physical layer", which is the physical means to connect computers. It mainly specifies some electrical characteristics of the network, which is responsible for transmitting electrical signals of 0 and 1.

Data connection layer

Simple 0 and 1 have no meaning, so our users will give them some specific meanings and specify the way to interpret electrical signals: for example, how many electrical signals are counted as a group? What is the meaning of each signal bit? This is the function of the "data link layer". It determines the grouping mode and representative meaning of 0 and 1 transmitted by the physical layer above the "physical layer". In the early days, each company had its own way of grouping electrical signals. Gradually, a protocol called "Ethernet" dominated.

Ethernet stipulates that a group of electrical signals form a data packet called a "Frame". Each Frame is divided into two parts: header and data. The header contains some description items of the packet, such as sender, receiver, data type, etc "Data" is the specific content of the data packet. " The length of the header is fixed to 18 bytes The length of "data", the shortest is 46 bytes and the longest is 1500 bytes. Therefore, the minimum length of the whole "Frame" is 64 bytes and the maximum length is 1518 bytes. If the data is very long, it must be divided into multiple frames for transmission.

So, how do the sender and receiver identify? Ethernet stipulates that all equipment connected to the network must have a "network card" interface. Packets must be transferred from one network card to another. The address of the network card is the sending address and receiving address of the data packet, which is called the MAC address. When each network card leaves the factory, it has a unique MAC address in the world, with a length of 48 binary bits, usually represented by 12 hexadecimal numbers. The first six hexadecimal numbers are the manufacturer's number, and the last six are the network card serial number of the manufacturer. With the MAC address, you can locate the network card and the path of the data packet.

We will obtain the MAC address of the receiver through the ARP protocol. After having the MAC address, how can we accurately send the data to the receiver? In fact, Ethernet here adopts a very "original" way. It does not accurately send the data packet to the receiver, but sends it to all computers in the network. Each computer reads the "header" of the packet, finds the MAC address of the receiver, and then compares it with its own MAC address. If the two are the same, accept the packet for further processing, Otherwise, discard the packet. This transmission method is called "broadcasting".

network layer

According to the rules of Ethernet protocol, we can rely on MAC address to send data to the outside. Theoretically, depending on the MAC address, your computer's network card can find the network card of a computer in another corner of the world. However, a major defect of this approach is that Ethernet uses broadcast to send data packets. All members have one "packet", which is not only inefficient, but also the data sent can only be limited to the sub network where the sender is located. In other words, if two computers are not in the same subnet, the broadcast cannot be transmitted. This design is reasonable and necessary, because it is unrealistic if every computer on the Internet will receive all data packets sent and received on the Internet.

Therefore, we must find a way to distinguish which MAC addresses belong to the same subnet and which are not. If it is the same sub network, it will be sent by broadcast, otherwise it will be sent by "routing". This led to the birth of the "network layer". Its function is to introduce a new set of addresses, so that we can distinguish whether different computers belong to the same subnet. This set of addresses is called "network address" and referred to as "website".

After the emergence of "network layer", each computer has two kinds of addresses, one is MAC address, the other is network address. There is no connection between the two addresses. The MAC address is bound to the network card, and the network address is assigned by the network administrator. The network address helps us determine the subnet where the computer is located, and the MAC address sends the data packet to the target network card in the subnet. Therefore, it can be logically inferred that the network address must be processed first, and then the MAC address.

The protocol that specifies the network address is called IP protocol. The address defined by it is called IP address. At present, the fourth version of IP protocol, referred to as IPv4, is widely used. The IPv4 version stipulates that the network address consists of 32 binary bits. We usually use the decimal number divided into four segments to represent the IP address, from 0.0.0.0 to 255.255.255.255.

Data sent according to IP protocol is called IP packet. IP packets are also divided into "header" and "data": the "header" part mainly includes version, length, IP address and other information, and the "data" part is the specific content of IP packets. The length of the "header" part of the IP packet is 20 to 60 bytes, and the maximum total length of the whole packet is 65535 bytes.

Transport layer

With MAC address and IP address, we can establish communication on any two hosts on the Internet. But the problem is that many programs on the same host need to use the network to send and receive data. For example, QQ and browser need to connect to the Internet and send and receive data. How can we distinguish which program a data packet belongs to? In other words, we also need a parameter to indicate which program (process) the packet is used by. This parameter is called "port", which is actually the number of each program using the network card. Each packet is sent to a specific port of the host, so different programs can get the data they need.

"Port" is an integer between 0 and 65535, exactly 16 binary bits. Ports from 0 to 1023 are occupied by the system. Users can only select ports greater than 1023. With IP and port, we can uniquely determine a program on the Internet, and then realize program communication between networks.

We must add port information to the packet, which requires a new protocol. The simplest implementation is called UDP protocol. Its format is almost to add the port number in front of the data. UDP packets are also composed of "header" and "data": the "header" part mainly defines the sending port and receiving port, and the "data" part is the specific content. UDP packet is very simple. The "header" part has only 8 bytes in total, and the total length is no more than 65535 bytes. It is just put into an IP packet.

The advantage of UDP protocol is relatively simple and easy to implement, but the disadvantage is poor reliability. Once the data packet is sent, it is impossible to know whether the other party receives it. In order to solve this problem and improve network reliability, TCP protocol was born. TCP protocol can ensure that data will not be lost. Its disadvantages are complex process, difficult implementation and consuming more resources. TCP packets have no length limit and can be infinitely long in theory. However, in order to ensure the efficiency of the network, the length of TCP packets will not exceed the length of IP packets, so as to ensure that a single TCP packet does not have to be segmented.

application layer

The application receives the data from the "transport layer", and then unpacks the data. Because the Internet is an open architecture and has a wide variety of data sources, the data format of communication must be specified in advance, otherwise the receiver can't get the real data content. " The function of "application layer" is to specify the data format used by the application, such as Email, HTTP, FTP and other protocols commonly used over TCP protocol. These protocols constitute the application layer of internet protocol.

As shown in the figure below, the header information of each layer protocol will be added in turn during the transmission of the sender's HTTP data through the Internet, and the receiver will unpack the data according to the protocol after receiving the data packet.

socket

Socket is the process communication mechanism of BSD UNIX, commonly referred to as "socket". It is used to describe IP address and port. It is a handle to the communication chain. Socket can be understood as the API of TCP/IP network. It defines many functions or routines that programmers can use to develop applications on TCP/IP network. Applications running on computers usually send requests to or respond to network requests through "sockets".

socket diagram

Socket is an intermediate software abstraction layer for communication between application layer and TCP/IP protocol family. In the design mode, socket is actually a facade mode. It hides the complex TCP/IP protocol family behind the socket. For users, they only need to call the relevant functions specified in the socket to let the socket organize the data conforming to the specified protocol and then communicate.

  • Socket is also called "socket". Applications usually send requests to the network or answer network requests through "socket"
  • There are two commonly used Socket types: streaming Socket and datagram Socket. Streaming is a connection oriented Socket for connection oriented TCP service applications, and datagram Socket is a connectionless Socket for connectionless UDP service applications
  • TCP: reliable, connection oriented and slow
  • UDP: not very reliable. It's fast

For example: TCP is like cash on delivery express. You must see you before you send it home. UDP is like an express cabinet that goes away as soon as it is thrown. Although you can't receive it, UDP is generally used for live broadcasting.

TCP programming


TCP protocol

TCP/IP(Transmission Control Protocol/Internet Protocol), i.e. transmission control protocol / inter network protocol, is a connection oriented (connection oriented), reliable and byte stream based Transport layer communication protocol. Because it is a connection oriented protocol, data is transmitted like water flow, and there will be packet sticking problem.

TCP server application example

A TCP server can connect many clients at the same time. For example, users all over the world use their browser on their computer to access Taobao. Because it is very convenient and efficient to create multiple goroutines in Go language to realize concurrency, we can create a goroutine to process every link.

Processing flow of TCP server program:

1. Listening port
2. Receive client request to establish link
3. Create goroutine processing links.


The TCP server code implemented by using the net package of Go language is as follows:

package main

import (
	"bufio"
	"fmt"
	"net"
)

// TCP server

//process handler
func process(conn net.Conn) {
	defer conn.Close()
	for {
		reader := bufio.NewReader(conn)
		var buf [128]byte
		// Read data
		read, err := reader.Read(buf[:])
		if err != nil {
			fmt.Println("read from client failed,err =",err)
		}
		recvStr := string(buf[:read])
		fmt.Println("received client Data sent from terminal:",recvStr)
		conn.Write([]byte(recvStr))
	}
}

func main() {
	listen, err := net.Listen("tcp", "127.0.0.1:20000")
	if err != nil {
		fmt.Println("listen failed,err =",err)
		return
	}
	for {
		// Establish connection
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept failed,err =",err)
			continue
		}
		go process(conn)
	}
}

TCP client application example


The process of TCP communication with a TCP client is as follows:

1. Establish a link with the server
2. Send and receive data
3. Close the link

The TCP client code implemented by using the net package of Go language is as follows:

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

// TCP client
func main() {
	dial, err := net.Dial("tcp", "127.0.0.1:20000")
	if err != nil {
		fmt.Println("dial failed,err =",err)
		return
	}
	// Close connection
	defer dial.Close()
	inputReader := bufio.NewReader(os.Stdin)
	for {
		// Read user input
		readString, _ := inputReader.ReadString('\n')
		trim := strings.Trim(readString, "\r\n")
		// Enter q if you want to exit
		if strings.ToUpper(trim) == "Q" {
			return
		}
		// send data
		_, err := dial.Write([]byte(trim))
		if err != nil {
			return
		}
		buf := [512]byte{}
		n, err := dial.Read(buf[:])
		if err != nil {
			fmt.Println("recv failed,err =",err)
			return
		}
		fmt.Println(string(buf[:n]))
	}
}

build the two programs into two. exe files. Click the server first, and then the client to send messages

TCP sticky packet (sticky packet) problem

Server code

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 code

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))
    }
}  

Save the above code and compile them respectively. Start the server first and then the client. You can see the output results of the server as follows:

Received data from client: 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?
Received data from client: 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?
Received data from client: Hello, Hello. How are you?Hello, Hello. How are you?
Received data from client: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
Received data from client: Hello, Hello. How are you?Hello, Hello. How are you?

The client sends 20 pieces of data, but the server receives no 20 pieces of data, but multiple pieces of information are bonded together.

Causes 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.

Packet sticking may occur at both sender and receiver

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.

resolvent

The key to "sticky 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.

We can define a protocol ourselves. For example, the first four bytes of a packet are the packet header, which stores the length of the transmitted data.

// Encode encodes the message
func Encode(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
}

// Decode decode message
func Decode(reader *bufio.Reader) (string, error) {
    // Read the length of the message
    lengthByte, _ := reader.Peek(4) // Read the first 4 bytes of data
    lengthBuff := bytes.NewBuffer(lengthByte)
    var length int32
    err := binary.Read(lengthBuff, binary.LittleEndian, &length)
    if err != nil {
        return "", err
    }
    // Buffered returns the number of bytes that are currently readable in the buffer.
    if int32(reader.Buffered()) < length+4 {
        return "", err
    }

    // Read real message data
    pack := make([]byte, int(4+length))
    _, err = reader.Read(pack)
    if err != nil {
        return "", err
    }
    return string(pack[4:]), nil
}  

Next, the server and client use the Decode and Encode functions of the proto package defined above to process data respectively.

The server code is as follows:

func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        msg, err := proto.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)
    }
}

The client code is as follows:

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?`
        data, err := proto.Encode(msg)
        if err != nil {
            fmt.Println("encode msg failed, err:", err)
            return
        }
        conn.Write(data)
    }
}  

UDP programming

UDP protocol

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.

UDP server application example

The net package of Go language is used to implement UDP server. The code is as follows:

package main

import (
	"fmt"
	"net"
)

// UDP server
func main() {
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 30000,
	})
	if err != nil {
		fmt.Println("listen failed,err =",err)
		return
	}
	defer listen.Close()
	for {
		var data [1024]byte
		// receive data 
		n, addr, err := listen.ReadFromUDP(data[:])
		if err != nil {
			fmt.Println("read udp failed,err =",err)
			continue
		}
		fmt.Printf("data:%v add:%v count:%v\n",string(data[:n]),addr,n)
		// send data
		_, err = listen.WriteToUDP(data[:n], addr)
		if err != nil {
			fmt.Println("write to udp failed,err =",err)
			continue
		}
	}
}

UDP client application example

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

// UDP client
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()
	inputReader := bufio.NewReader(os.Stdin)
	// Read user input
	for {
		readString, _ := inputReader.ReadString('\n')
		trim := strings.Trim(readString, "\r\n")
		// Enter q if you want to exit
		if strings.ToUpper(trim) == "Q" {
			return
		}
		// send data
		_, err = socket.Write([]byte(trim))
		if err != nil {
			fmt.Println("Failed to send data,err =",err)
			return
		}
		data := make([]byte,4096)
		// receive data 
		n, remoAddr, 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",string(data[:n]),remoAddr,n)
	}
}


HTTP programming

web Workflow

  • The working principle of Web server can be simply summarized as
    • 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) 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 communication simulation

Server

package main

import (
	"fmt"
	"net/http"
)

//handler function
func myHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println(r.RemoteAddr,"Connection succeeded")
	// Request mode
	fmt.Println("method:",r.Method)
	// /go
	fmt.Println("url:",r.URL.Path)
	fmt.Println("header:",r.Header)
	fmt.Println("body:",r.Body)
}

func main() {
	// http://127.0.0.1:80000/go
	// Write callback function separately
	http.HandleFunc("/go",myHandler)
	// addr: listening address
	// handler: callback function
	http.ListenAndServe("127.0.0.1:8000",nil)
}

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("Failed to receive server information,err =",err)
			return
		} else {
			fmt.Println("Read complete")
			res := string(buf[:n])
			fmt.Println("The information sent by the server is:",res)
			break
		}
	}
}

Open the server and click the client as follows


WebSocket programming

What is WebSocket

  • WebSocket is a protocol for full duplex communication over a single TCP connection
  • WebSocket makes the data exchange between the client and the server easier, and allows the server to actively push data to the client
  • In the WebSocket API, the browser and server only need to complete a handshake, and they can directly create a persistent connection and conduct two-way data transmission
    Third party packages need to be installed:
    • cmd: go get -u -v github.com/gorilla/websocket

Example of chat room

Create four go files connection.go|data.go|hub.go|server.go in the same directory

server.go

package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

func main() {
	router := mux.NewRouter()
	go h.run()
	router.HandleFunc("/ws",myws)
	err := http.ListenAndServe("127.0.0.1:8080",router)
	if err != nil {
		fmt.Println("err:",err)
	}
}

hub.go

package main

import "encoding/json"

var h = hub{
	c: make(map[*connection]bool),
	u: make(chan *connection),
	b: make(chan []byte),
	r: make(chan *connection),
}

type hub struct {
	c map[*connection]bool
	b chan []byte
	r chan *connection
	u chan *connection
}

func (h *hub) run() {
	for {
		select {
		case c := <-h.r:
			h.c[c] = true
			c.data.Ip = c.ws.RemoteAddr().String()
			c.data.Type = "handshake"
			c.data.UserList = user_list
			data_b, _ := json.Marshal(c.data)
			c.sc <- data_b
		case c := <-h.u:
			if _, ok := h.c[c]; ok {
				delete(h.c, c)
				close(c.sc)
			}
		case data := <-h.b:
			for c := range h.c {
				select {
				case c.sc <- data:
				default:
					delete(h.c, c)
					close(c.sc)
				}
			}
		}
	}
}

data.go

package main

type Data struct {
	Ip       string   `json:"ip"`
	User     string   `json:"user"`
	From     string   `json:"from"`
	Type     string   `json:"type"`
	Content  string   `json:"content"`
	UserList []string `json:"user_list"`
}

connection.go

package main
import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/websocket"
)

type connection struct {
	ws   *websocket.Conn
	sc   chan []byte
	data *Data
}

var wu = &websocket.Upgrader{ReadBufferSize: 512,
	WriteBufferSize: 512, CheckOrigin: func(r *http.Request) bool { return true }}

func myws(w http.ResponseWriter, r *http.Request) {
	ws, err := wu.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	c := &connection{sc: make(chan []byte, 256), ws: ws, data: &Data{}}
	h.r <- c
	go c.writer()
	c.reader()
	defer func() {
		c.data.Type = "logout"
		user_list = del(user_list, c.data.User)
		c.data.UserList = user_list
		c.data.Content = c.data.User
		data_b, _ := json.Marshal(c.data)
		h.b <- data_b
		h.r <- c
	}()
}

func (c *connection) writer() {
	for message := range c.sc {
		c.ws.WriteMessage(websocket.TextMessage, message)
	}
	c.ws.Close()
}

var user_list = []string{}

func (c *connection) reader() {
	for {
		_, message, err := c.ws.ReadMessage()
		if err != nil {
			h.r <- c
			break
		}
		json.Unmarshal(message, &c.data)
		switch c.data.Type {
		case "login":
			c.data.User = c.data.Content
			c.data.From = c.data.User
			user_list = append(user_list, c.data.User)
			c.data.UserList = user_list
			data_b, _ := json.Marshal(c.data)
			h.b <- data_b
		case "user":
			c.data.Type = "user"
			data_b, _ := json.Marshal(c.data)
			h.b <- data_b
		case "logout":
			c.data.Type = "logout"
			user_list = del(user_list, c.data.User)
			data_b, _ := json.Marshal(c.data)
			h.b <- data_b
			h.r <- c
		default:
			fmt.Print("========default================")
		}
	}
}

func del(slice []string, user string) []string {
	count := len(slice)
	if count == 0 {
		return slice
	}
	if count == 1 && slice[0] == user {
		return []string{}
	}
	var n_slice = []string{}
	for i := range slice {
		if slice[i] == user && i == count {
			return slice[:count]
		} else if slice[i] == user {
			n_slice = append(slice[:i], slice[i+1:]...)
			break
		}
	}
	fmt.Println(n_slice)
	return n_slice
}

local.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <style>
        p {
            text-align: left;
            padding-left: 20px;
        }
    </style>
</head>
<body>
<div style="width: 800px;height: 600px;margin: 30px auto;text-align: center">
    <h1>Demo chat room</h1>
    <div style="width: 800px;border: 1px solid gray;height: 300px;">
        <div style="width: 200px;height: 300px;float: left;text-align: left;">
            <p><span>Currently online:</span><span id="user_num">0</span></p>
            <div id="user_list" style="overflow: auto;">
            </div>
        </div>
        <div id="msg_list" style="width: 598px;border:  1px solid gray; height: 300px;overflow: scroll;float: left;">
        </div>
    </div>
    <br>
    <textarea id="msg_box" rows="6" cols="50" onkeydown="confirm(event)"></textarea><br>
    <input type="button" value="send out" onclick="send()">
</div>
</body>
</html>
<script type="text/javascript">
    var uname = prompt('enter one user name', 'user' + uuid(8, 16));
    var ws = new WebSocket("ws://127.0.0.1:8080/ws");
    ws.onopen = function () {
        var data = "System message: connection established successfully";
        listMsg(data);
    };
    ws.onmessage = function (e) {
        var msg = JSON.parse(e.data);
        var sender, user_name, name_list, change_type;
        switch (msg.type) {
            case 'system':
                sender = 'System message: ';
                break;
            case 'user':
                sender = msg.from + ': ';
                break;
            case 'handshake':
                var user_info = {'type': 'login', 'content': uname};
                sendMsg(user_info);
                return;
            case 'login':
            case 'logout':
                user_name = msg.content;
                name_list = msg.user_list;
                change_type = msg.type;
                dealUser(user_name, change_type, name_list);
                return;
        }
        var data = sender + msg.content;
        listMsg(data);
    };
    ws.onerror = function () {
        var data = "System message : Error ,Please exit and try again.";
        listMsg(data);
    };
    function confirm(event) {
        var key_num = event.keyCode;
        if (13 == key_num) {
            send();
        } else {
            return false;
        }
    }
    function send() {
        var msg_box = document.getElementById("msg_box");
        var content = msg_box.value;
        var reg = new RegExp("\r\n", "g");
        content = content.replace(reg, "");
        var msg = {'content': content.trim(), 'type': 'user'};
        sendMsg(msg);
        msg_box.value = '';
    }
    function listMsg(data) {
        var msg_list = document.getElementById("msg_list");
        var msg = document.createElement("p");
        msg.innerHTML = data;
        msg_list.appendChild(msg);
        msg_list.scrollTop = msg_list.scrollHeight;
    }
    function dealUser(user_name, type, name_list) {
        var user_list = document.getElementById("user_list");
        var user_num = document.getElementById("user_num");
        while(user_list.hasChildNodes()) {
            user_list.removeChild(user_list.firstChild);
        }
        for (var index in name_list) {
            var user = document.createElement("p");
            user.innerHTML = name_list[index];
            user_list.appendChild(user);
        }
        user_num.innerHTML = name_list.length;
        user_list.scrollTop = user_list.scrollHeight;
        var change = type == 'login' ? 'go online' : 'Offline';
        var data = 'System message: ' + user_name + ' already' + change;
        listMsg(data);
    }
    function sendMsg(msg) {
        var data = JSON.stringify(msg);
        ws.send(data);
    }
    function uuid(len, radix) {
        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
        var uuid = [], i;
        radix = radix || chars.length;
        if (len) {
            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
        } else {
            var r;
            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
            uuid[14] = '4';
            for (i = 0; i < 36; i++) {
                if (!uuid[i]) {
                    r = 0 | Math.random() * 16;
                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                }
            }
        }
        return uuid.join('');
    }
</script>

function

1. First execute go run connection.go data.go hub.go server.go on the console

2. Then open local.html


Reference documents: go language Chinese document

Keywords: Go socket Back-end Network Protocol

Added by yarnold on Sun, 28 Nov 2021 05:29:58 +0200