赞
踩
网络编程介绍
服务器监听
在Go的net包中,Listen函数用于创建并返回一个网络监听器(Listener),以监听指定网络地址和端口上的连接请求。该函数的函数原型如下:
func Listen(network, address string) (Listener, error)
参数说明:
返回值说明:
通过Listen函数创建得到的网络监听器是Listener类型的,该类型是一个接口类型,其定义如下:
type Listener interface {
// Accept waits for and returns the next connection to the listener.
Accept() (Conn, error)
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
Close() error
// Addr returns the listener's network address.
Addr() Addr
}
Listener接口中各方法说明:
当使用Listen函数创建TCP类型的监听器时,其返回的监听器底层具体的类型是TCPListener,其定义如下:
type TCPListener struct {
fd *netFD
lc ListenConfig
}
TCPListener结构体各字段说明:
TCPListener结构体中的fd字段是netFD类型的,其定义如下:
type netFD struct {
pfd poll.FD
// immutable until Close
family int
sotype int
isConnected bool // handshake completed or use of association with peer
net string
laddr Addr
raddr Addr
}
netFD结构体各字段说明:
服务器监听示例
服务器监听示例如下:
package main import ( "fmt" "net" ) func main() { // 服务器监听 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") }
说明一下:
0.0.0.0
(通常表示为INADDR_ANY)地址,这样服务器就可以同时监听和接受来自不同网络接口的连接请求,而不需要为每个接口分别创建监听套接字。客户端连接服务器
在Go的net包中,Dial函数用于客户端应用程序与远程服务器建立连接。该函数的函数原型如下:
func Dial(network, address string) (Conn, error)
参数说明:
返回值说明:
通过Dial函数建立得到的连接Conn类型的,该类型是一个接口类型,其定义如下:
type Conn interface { // Read reads data from the connection. Read(b []byte) (n int, err error) // Write writes data to the connection. Write(b []byte) (n int, err error) // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. Close() error // LocalAddr returns the local network address, if known. LocalAddr() Addr // RemoteAddr returns the remote network address, if known. RemoteAddr() Addr // SetDeadline sets the read and write deadlines associated // with the connection. It is equivalent to calling both // SetReadDeadline and SetWriteDeadline. SetDeadline(t time.Time) error // SetReadDeadline sets the deadline for future Read calls // and any currently-blocked Read call. SetReadDeadline(t time.Time) error // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. SetWriteDeadline(t time.Time) error }
Conn接口中各方法说明:
当使用Dial函数与TCP服务器建立连接时,其返回的网络连接底层具体的类型是TCPConn,其定义如下:
type TCPConn struct {
conn
}
TCPConn结构体中仅嵌套了一个conn类型的匿名结构体,其定义如下:
type conn struct {
fd *netFD
}
可以看到,conn结构体中的fd字段与TCPListener结构体中的fd字段的类型相同,它们都是对底层网络文件描述符的封装,提供了对网络连接的读写和控制操作。
客户端连接服务器示例
客户端连接服务器示例如下:
package main import ( "fmt" "net" ) func main() { // 客户端连接服务器 conn, err := net.Dial("tcp", "127.0.0.1:8081") if err != nil { fmt.Printf("connect server error, err = %v\n", err) return } defer conn.Close() fmt.Println("connect server success...") }
说明一下:
服务端获取连接
在创建TCP网络监听器后,调用Listener接口的Accept方法,本质调用的是TCPListener的Accept方法,该方法用于从底层获取下一个已经建立好的连接给监听器,如果底层没有建立好的连接则会进行阻塞等待。该方法的原型如下:
func (l *TCPListener) Accept() (Conn, error)
返回值说明:
服务端获取连接示例
服务端获取连接示例如下:
package main import ( "fmt" "net" ) func process(conn net.Conn) { defer conn.Close() fmt.Printf("handle a link %v...\n", conn.RemoteAddr()) } func main() { // 服务器监听 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") for { fmt.Println("waiting client connect...") // 服务端获取连接 conn, err := listen.Accept() if err != nil { fmt.Printf("accept error, err = %v\n", err) continue } fmt.Printf("get a link from %v...\n", conn.RemoteAddr()) // 开启新协程为客户端提供服务 go process(conn) } }
说明一下:
向连接中写入数据
在创建TCP连接后,调用Conn接口的Write方法,本质调用的是TCPConn的Write方法,该方法用于向连接中写入数据。该方法的原型如下:
func (c *TCPConn) Write(b []byte) (int, error)
参数说明:
返回值说明:
从连接中读取数据
在创建TCP连接后,调用Conn接口的Read方法,本质调用的是TCPConn的Read方法,该方法用于从连接中读取数据。该方法的原型如下:
func (c *TCPConn) Read(b []byte) (int, error)
参数说明:
返回值说明:
关闭连接/监听器
为了避免网络文件描述符泄露,TCP网络监听器和TCP连接在使用完毕后都需要及时将其关闭,对应调用的分别是TCPListener和TCPConn的Close方法,这两个方法的原型如下:
func (l *TCPListener) Close() error
func (c *TCPConn) Close() error
返回值说明:
效果展示
为了演示使用net包实现网络通信,下面实现了一个简易的TCP回声服务器,其功能如下:
最终效果如下:
服务端处理逻辑
服务端处理逻辑如下:
服务端代码如下:
package main import ( "fmt" "io" "net" ) func process(conn net.Conn) { defer conn.Close() data := make([]byte, 1024) for { // 1、读取客户端发来的数据 n, err := conn.Read(data) if err != nil { if err == io.EOF { fmt.Printf("client %v quit\n", conn.RemoteAddr()) } else { fmt.Printf("read client message error, err = %v\n", err) } return } fmt.Printf("client message[%v]: %v\n", conn.RemoteAddr(), string(data[:n])) // 2、发送数据给客户端 len, err := conn.Write(data[:n]) if err != nil || len != n { fmt.Printf("send back message error, err = %v\n", err) return } } } func main() { // 服务器监听 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") for { fmt.Println("waiting client connect...") // 服务端获取连接 conn, err := listen.Accept() if err != nil { fmt.Printf("accept error, err = %v\n", err) continue } fmt.Printf("get a link from %v...\n", conn.RemoteAddr()) // 开启新协程为客户端提供服务 go process(conn) } }
说明一下:
客户端处理逻辑
客户端处理逻辑如下:
客户端代码如下:
package main import ( "bufio" "fmt" "net" "os" ) func main() { // 客户端连接服务器 conn, err := net.Dial("tcp", "127.0.0.1:8081") if err != nil { fmt.Printf("connect server error, err = %v\n", err) return } defer conn.Close() fmt.Println("connect server success...") reader := bufio.NewReader(os.Stdin) data := make([]byte, 1024) for { // 1、读取用户输入 str, err := reader.ReadString('\n') if err != nil { fmt.Printf("read input error, err = %v\n", err) continue } str = str[:len(str)-2] // 去掉\r\n if str == "exit" { fmt.Printf("exit success...") break } // 2、发送数据给服务端 n, err := conn.Write([]byte(str)) if err != nil || n != len(str) { fmt.Printf("send message error, err = %v\n", err) continue } fmt.Printf("send %d byte message to server...\n", n) // 3、读取服务端发来的数据 n, err = conn.Read(data) if err != nil { fmt.Printf("read message error, err = %v\n", err) continue } fmt.Printf("server message: %v\n", string(data[:n])) } }
说明一下:
\r\n
作为换行符,因此客户端在每次读取一行用户输入的数据后需要将末尾的两个字符去掉。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。