socket特性
- 总是成对出现
- 是全双工的(同时支持收发)(两个channel绑在一起)
应用程序
- cs模式(客户端开发)
- bs模式(web开发)
net包api基础
都是客户端主动发数据(client request)
一共有3个soket。用于通信的有2个。另一个用于监听ip端口
// 服务端 import ( "net" ) listener, err := net.Listen("tcp", "127.0.0.1:8000") //生成监听socket defer listener.Close() conn, err := listener.Accept() //[**阻塞] 生成读写socket defer conn.Close() buf := make([]byte, 4096) n, err := conn.Read(buf) //[**阻塞] //读客户端数据(client request) conn.Write(buf[:n]) //写数据给客户端
//客户端 conn, err := net.Dial("tcp", "127.0.0.1:8000") defer conn.Close() conn.Write([]byte("Are you Ready?")) //写数据 buf := make([]byte, 4096) //读数据 n, err := conn.Read(buf) fmt.Println("服务器回发:", string(buf[:n]))
并发版的server/client
// 并发版的server listener, err := net.Listen("tcp", "localhost:3000") defer listener.Close() for { conn, err := listener.Accept() //阻塞,获取读写socket: conn if err != nil { fmt.Println("listener.Accept err:", err) return } go HandlerConnect(conn) } func HandlerConnect(conn net.Conn) { defer conn.Close() // 获取连接的客户端 Addr addr := conn.RemoteAddr() fmt.Println(addr, "客户端成功连接!") // 循环读取客户端发送数据 buf := make([]byte, 4096) for { n, err := conn.Read(buf) if "exit\n" == string(buf[:n]) || "exit\r\n" == string(buf[:n]) { fmt.Println("服务器接收的客户端退出请求,服务器关闭") return } if n == 0 { fmt.Println("服务器检测到客户端已关闭,断开连接!!!") return } if err != nil { fmt.Println("conn.Read err:", err) return } fmt.Println("服务器读到数据:", string(buf[:n])) conn.Write([]byte(strings.ToUpper(string(buf[:n])))) } }
//并发版的客户端 func main() { conn, err := net.Dial("tcp", "192.168.15.78:8001") if err != nil { fmt.Println("net.Dial err:", err) return } defer conn.Close() // 获取用户键盘输入( stdin ),将输入数据发送给服务器 go func() { str := make([]byte, 4096) for { n, err := os.Stdin.Read(str) if err != nil { fmt.Println("os.Stdin.Read err:", err) continue } //写给服务器, 读多少,写多少! conn.Write(str[:n]) } }() // 回显服务器回发的大写数据 buf := make([]byte, 4096) for { n, err := conn.Read(buf) if n == 0 { fmt.Println("检查到服务器关闭,客户端也关闭") return } if err != nil { fmt.Println("conn.Read err:", err) return } fmt.Println("客户端读到服务器回发:", string(buf[:n])) } }