Go Socket Operational Notes

Keywords: Go socket network Programming Unix

concept

The first thing Socket does is translate it into a hole or socket. Two programs on the network exchange data through a two-way communication connection, one end of which is called a socket.
The essence of Socket is programming interface, which is an IPC interface. (IPC: Inter-process communication) Unlike other IPC methods, it allows multiple processes to establish communications over the network. Whether the two sides of the communication become irrelevant on the same machine.

How Socket Communicates

Socket provides network links through TCP/IP protocol family. Socket is the abstract layer between application and transport layer, encapsulating the TCP/IP protocol family. With a simple set of interfaces, it can communicate through network links. The following is a classic picture on the Internet. Users do not need to know all kinds of complex functional protocols of TCP/IP. They can do all the work directly by using the interface provided by Socket.

Socket communication process

  • Server: First, the server needs to initialize the Socket, then bind with the port, listen on the port, call accept to block, and wait for the client to connect. At this point, if a client initializes a Socket and then connects to the server, if the connection is successful, then the connection between the client and the server is established.
  • Client: The client sends the data request, the server receives the request and processes the request, then sends the response data to the client, the client reads the data, finally closes the connection, and the interaction ends.

The main interface provided by Socket

  • Initialization: int socket(int domain, int type, int protocol)
  • Binding: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • Listening: listen()
  • Acceptance request: accept()

The meaning of the specific parameters is not expanded, we mainly look at how go operates socket.

How do Go operate Socket

The concept of Socket is briefly introduced above. In go language, we can easily use net package to operate. In fact, go's net package is to re-encapsulate the above Socket interface, so that we can easily establish the Socket connection and use the connection communication. Code directly

Common function

//Common functions are used to define Socket-type ip ports.
const(
    Server_NetWorkType = "tcp"
    Server_Address = "127.0.0.1:8085"
    Delimiter = '\t'
)

// Writing data to conn can be used to transfer data from client to server or return data from server to client.
func Write(conn net.Conn, content string)(int, error){
    var buffer bytes.Buffer
    buffer.WriteString(content)
    buffer.WriteByte(Delimiter)

    return conn.Write(buffer.Bytes())
}

// Read the byte stream from conn, marked by the terminator above
func Read(conn net.Conn)(string, error){
    readBytes := make([]byte,1)
    var buffer bytes.Buffer
    for{
        if _,err := conn.Read(readBytes);err != nil{
            return "", err
        }
        readByte := readBytes[0]
        if readByte == Delimiter{
            break
        }
        buffer.WriteByte(readByte)
    }

    return buffer.String(), nil
}

Server:

func main() {
    // The net listen function passes in the socket type and the ip port and returns the listener object
    listener, err := net.Listen(socket.Server_NetWorkType,socket.Server_Address)
    if err == nil{
        // Loop waiting for client access
        for{
            conn,err := listener.Accept()
            if err == nil{
                // Asynchronous execution is turned on directly once there is an external request and there is no error
                go handleConn(conn)
            }
        }
    }else{
        fmt.Println("server error", err)
    }
    defer listener.Close()
}

func handleConn(conn net.Conn){
    for {
        // Setting read timeout
        conn.SetReadDeadline(time.Now().Add(time.Second * 2))
        // Call the public method read to get the message from the client.
        if str, err := socket.Read(conn); err == nil{
            fmt.Println("client:",conn.RemoteAddr(),str)
            // Pass a message to the client through the write method
            socket.Write(conn,"server got:"+str)
        }


    }
}

Client

func main() {
    // Call dial-in ip port in net package for dial-up connection and get conn after three handshakes
    conn,err := net.Dial(socket.Server_NetWorkType, socket.Server_Address)
    if err != nil{
        fmt.Println("Client create conn error err:", err)
    }
    defer conn.Close()
    //Delivery of messages to the server
    socket.Write(conn,"aaaa")
    //Read the message returned by the server
    if str, err := socket.Read(conn);err == nil{
        fmt.Println(str)
    }

}

As you can see, the code above is simple. Socket communication can be easily realized by using net package.

Simple Source View

We can see that the socket described above requires at least the basic steps of creating (socket function) binding (bind function) listening (listen function). These steps are actually encapsulated in our net package, to our code customers from the net.Listen function to see the source code. Because there are too many code calls and only some critical code segments are pasted.
First listen decides whether to listen on tcp or unix. After some column calls, we go to the sysSocket method, which calls the socket method of the system to initialize the socket object and return a socket identifier. This identifier is then used for binding listening. Finally, the listener object is returned.

func Listen(network, address string) (Listener, error) {
    addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
    if err != nil {
        return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
    }
    var l Listener
    switch la := addrs.first(isIPv4).(type) {
    case *TCPAddr:
        // Monitoring TCP 
        l, err = ListenTCP(network, la)
    case *UnixAddr:
        l, err = ListenUnix(network, la)
    default:
        return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
    }
    if err != nil {
        return nil, err // l is non-nil interface containing nil pointer
    }
    return l, nil
}
// Is the final system method invoked similar to the socket initialization method?
func sysSocket(family, sotype, proto int) (int, error) {
    // See ../syscall/exec_unix.go for description of ForkLock.
    syscall.ForkLock.RLock()
    s, err := socketFunc(family, sotype, proto)
    if err == nil {
        syscall.CloseOnExec(s)
    }
    syscall.ForkLock.RUnlock()
    if err != nil {
        return -1, os.NewSyscallError("socket", err)
    }
    if err = syscall.SetNonblock(s, true); err != nil {
        poll.CloseFunc(s)
        return -1, os.NewSyscallError("setnonblock", err)
    }
    return s, nil
}

Posted by jasonc310771 on Sat, 04 May 2019 07:30:39 -0700