Golang Socket programming under Linux principle analysis and code implementation

  After the introduction of standard POSIX, socket in the major mainstream OS platform have been well supported. The Golang Runtime is built cross-platform programming language, Go provides developers with the Socket API is built on top of the native operating system Socket interface. But Golang the Socket interface characteristics and behavior of the native operating system has a number of different interfaces. This will combine a simple network chat program analysis.

A, socket Introduction

       First, the premise can communicate between processes are processes can be uniquely identified when local communication can be used to uniquely identify PID, but this approach is not feasible in the network, we can uniquely identify a protocol by IP address and port number + process, and then the use of socket communication. socket communication process is as follows:

1. Create a server socket

2. Bind socket server and port number

3. The server listens to this port number

4. Start the server accept () for receiving a connection request from a client, if at this time connected to proceed, otherwise blocked here.

5. The client creates socket

6. Client by IP address and port number to connect the server, namely in the three-way handshake tcp

7. If the connection is successful, the client can send data to the server

8. The server reads the data sent by the client

9. Any active end can be disconnected

Two, socket programming

    With the abstract socket, when using TCP or UDP protocol web programming, it may be performed by the following manner.

Server pseudocode:

listenfd = socket(……)
bind(listenfd, ServerIp:Port, ……)
listen(listenfd, ……)
while(true) {
  conn = accept(listenfd, ……)
  receive(conn, ……)
  send(conn, ……)
}

Client pseudocode:

clientfd = socket(……)
connect(clientfd, serverIp:Port, ……)
send(clientfd, data)
receive(clientfd, ……)
close(clientfd)

  Above pseudo code, listenfd is to achieve server listening socket descriptor created, and bind the server process is to occupy the port, other ports to avoid being used by another process, listen method starts listening to port. The following while loop for processing client requests a steady stream, the method returns an Accept conn, used to distinguish client connection, after receiving and transmitting operation are achieved based on the conn. In fact, accept that and connect the client to complete the TCP three-way handshake together.

Three, golang the socket

      golang provides some network programming API, including Dial, Listen, Accept, Read, Write, Close the like.

3.1 Listen()

     The first to use server-side net.Listen () method to create a socket, bind and listen on port port.

func Listen(network, address string) (Listener, error) {
    var lc ListenConfig
    return lc.Listen(context.Background(), network, address)
}

  The above is a function of the source golang Listen provided, wherein the network represents a network protocol, such as tcp, tcp4, tcp6, udp, udp4, udp6 like. address for the address binding, returned Listener is actually a socket descriptor, error saved in the error message.

     The use of socket, bind and listen functions in Linux socket in to perform the same function.

int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int listen(int sockfd, int backlog);

3.2 Dial()

   When a client wants to initiate a connection, it will use net.Dial () method to connect.

func Dial(network, address string) (Conn, error) {
    var d Dialer
    return d.Dial(network, address)
}

  Wherein the network represents a network protocol, address for the address to establish a connection, return Conn actually identify each client, Conn., A definition of the interface golang:

type Conn interface {
    Read(b []byte) (n int, err error)
    Write(b []byte) (n int, err error)
    Close() error
    LocalAddr() Addr
    RemoteAddr() Addr
    SetDeadline(t time.Time) error
    SetReadDeadline(t time.Time) error
    SetWriteDeadline(t time.Time) error
}

 

type conn struct {
    fd *netFD
}

  Wherein netFD library is golang core network data structure, throughout all the API library golang network, on the bottom of the socket encapsulates shield the networks of different operating systems, so that the returned by Conn, we can use the provided golang the underlying function of the socket.

  To create a connection using the connect function in Linux socket in:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

3.3 Accept()

       When the server calls net.Listen () will start listening on the specified address, and the client calls net.Dial () initiates a connection request, then the server calls net.Accept () receives the request, the client and the client's connection here on the establishment of good the fact that this step is finished in TCP three-way handshake.

Accept() (Conn, error)

  golang the socket is actually non-blocking, but golang socket itself to do some processing, it appears to be blocked.

      Use accept function in the Linux socket to achieve the same functionality:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

3.4 Write()

      End side of the connection has been established, the next start read and write operations, conn.Write () to write data to the socket:

func (c *conn) Write(b []byte) (int, error) {
    if !c.ok() {
        return 0, syscall.EINVAL
    }
    n, err := c.fd.Write(b)
    if err != nil {
        err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return n, err
}

 Wherein write data is a binary byte stream, the returned data length n, stored ERR error.

    Linux socket corresponding send function is:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

3.5 Read()

     After the client after sending the data, the server may receive data, golang call conn.Read () to read data, the following source code:

func (c *conn) Read(b []byte) (int, error) {
    if !c.ok() {
        return 0, syscall.EINVAL
    }
    n, err := c.fd.Read(b)
    if err != nil && err != io.EOF {
        err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return n, err
}

  Write the parameters and the same meaning (), use recv function to accomplish this function in Linux socket in:

size_t recv(int sockfd, void *buf, size_t len, int flags);

3.6 Close()

     When the server or the client wants to close the socket, call the Close () method to close the connection.

func (c *conn) Close() error {
    if !c.ok() {
        return syscall.EINVAL
    }
    err := c.fd.Close()
    if err != nil {
        err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    }
    return err
}

  Linux socket using the close function in:

int close(int socketfd);

Four, golang for network chat program

4.1 server.go

package main

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

func main() {
    listener, err := net.Listen("tcp", "localhost:8848")
    if err != nil {
        log.Fatal(err)
    }
    go broadcaster()
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err)
            continue
        }
        go handleConn(conn)
    }
}

type client chan <- string // an outgoing message channel

var (
    entering = make(chan client)
    leaving = make(chan client)
    messages = make(chan string) // incoming messages from clients
)

func broadcaster() {
    clients := make(map[client]bool)
    for {
        select {
            // broadcast incoming message to all client's outgoing message channels
            case msg := <- messages:
                for cli := range clients {
                    cli <- msg
                }
            case cli := <- entering:
                clients[cli] = true
            case cli := <- leaving:
                delete(clients, cli)
                close(cli)
            }
    }
}

func handleConn(conn net.Conn) {
    ch := make(chan string) // outgoing clietnt messages
    go clientWriter(conn, ch)

    who := conn.RemoteAddr().String()
    ch <- "You are " + who
    messages <- who + " has arrived"
    entering <- ch

    input := bufio.NewScanner(conn)
    for input.Scan() {
        messages <- who + ": " + input.Text()
    }
    
    leaving <- ch
    messages <- who + " has left"
    conn.Close()
}

func clientWriter(conn net.Conn, ch <-chan string) {
    for msg := range ch {
        fmt.Fprintln(conn, msg)
    }
}

  end server provided three channel: entering, leaving and status messages for data shared between the access client goroutine, and send the message to leave the like. For each client connection, server will open a separate goroutine processing. Corresponding to the respective channel, it is also provided for a single broadcaster goroutine broadcast message and updates the client connection status.

4.2 client.go

package main

import (
	"io"
	"log"
	"net"
	"os"
)

func main()  {
	conn, err := net.Dial("tcp", "localhost:8848")
	if err != nil {
		log.Fatal(err)
	}
	done := make(chan struct{})
	go func() {
		io.Copy(os.Stdout, conn)
		log.Println("done")
		done <- struct{}{}
	}()
	mustCopy(conn, os.Stdin)
	conn.Close()
	<- done
}

func mustCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err!= nil {
		log.Fatal(err)
	}
}

  After compiling two source files, first start the server program that listens port 8848, and then to run multiple client programs. Each client program can send a message to the server and get a response. It was added to each client, as well as leaving a message sent will be broadcast to other online clients. Results as shown below.

 

 

 

Guess you like

Origin www.cnblogs.com/smarxdray/p/12002830.html