Socket is a layer interface provided after TCP/UDP encapsulation. We can use Socket to write the server and the client, and then let the client and the server establish TCP or UDP connection.
Unix Socket programming function interface
Socket programming in Unix/Linux is mainly realized by calling listen, accept, write read and other functions. The details are shown in the figure below:
UnixSocket
Socket programming model in Golang
Compared to Linux Socket programming, go Socket programming is much easier. The server can directly implement the Listen + Accept mode:
func connHandler(c net.Conn) {
for {
cnt, err := c.Read(buf)
c.Write(buf)
}
}
func main() {
server, err := net.Listen("tcp", ":1208")
for {
conn, err := server.Accept()
go connHandler(conn)
}
}Copy the code
The client calls Dial directly:
func connHandler(c net.Conn) {
for {
c.Write(...)
c.Read(...)
}
}
func main() {
conn, err := net.Dial("tcp", "localhost:1208")
connHandler(conn)
}Copy the code
Next we will use an example to show how to use GO for Socket programming. The specific code is stored on Github.
Implement a server that can accept different commands
We implement a server that accepts the following commands:
ping
The server will return “pong.”echo
The server returns the received stringquit
The server receives this command and closes the connection
The specific server code is as follows:
package main import ( "fmt" "net" "strings" ) func connHandler(c net.Conn) { if c == nil { return } buf := make([]byte, 4096) for { cnt, err := c.Read(buf) if err ! = nil || cnt == 0 { c.Close() break } inStr := strings.TrimSpace(string(buf[0:cnt])) inputs := strings.Split(inStr, " ") switch inputs[0] { case "ping": c.Write([]byte("pong\n")) case "echo": echoStr := strings.Join(inputs[1:], " ") + "\n" c.Write([]byte(echoStr)) case "quit": c.Close() break default: fmt.Printf("Unsupported command: %s\n", inputs[0]) } } fmt.Printf("Connection from %v closed. \n", c.RemoteAddr()) } func main() { server, err := net.Listen("tcp", ":1208") if err ! = nil { fmt.Printf("Fail to start server, %s\n", err) } fmt.Println("Server Started ..." ) for { conn, err := server.Accept() if err ! = nil { fmt.Printf("Fail to connect, %s\n", err) break } go connHandler(conn) } }Copy the code
After compiling the above server code and starting it, we use Telnet to test whether the server works properly and the result is as shown in the following figure:
We input the following three commands in Telnet respectively:
ping
echo hello, hbliu
quit
Client-side implementation
We can implement a client by ourselves to communicate with our server and realize functions similar to Telnet. The code is as follows:
package main import ( "bufio" "fmt" "net" "os" "strings" ) func connHandler(c net.Conn) { defer c.Close() reader := bufio.NewReader(os.Stdin) buf := make([]byte, 1024) for { input, _ := reader.ReadString('\n') input = strings.TrimSpace(input) if input == "quit" { return } c.Write([]byte(input)) cnt, err := c.Read(buf) if err ! = nil { fmt.Printf("Fail to read data, %s\n", err) continue } fmt.Print(string(buf[0:cnt])) } } func main() { conn, err := net.Dial("tcp", "localhost:1208") if err ! = nil { fmt.Printf("Fail to connect, %s\n", err) return } connHandler(conn) }Copy the code
We can compile the above code and start it to execute the commands supported by the server, as shown below:
TCP status conversion diagram
Where the dotted line indicates the state change of the passive party, and the thick red line indicates the state change of the active party:
State changes observed from a server-side client perspective:
References
- The relationship between CONNECT (), Listen (), and Accept () in TCP network programming
- The TCP thing (PART 1)
- Echo Service
- Go language TCP Socket programming