什么是Socket?
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
Socket分类:
为了满足不同的通信程序对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接字,以供用户在设计网络应用程序时根据不同的要求来选择。这三种套接为流式套接字(SOCK-STREAM)、数据报套接字(SOCK-DGRAM)和原始套接字(SOCK-RAW)。
1.流式套接字(TCP)
它提供了一种可靠的、面向连接的双向数据传输服务,实现了数据无差错、无重复的发送。流式套接字内设流量控制,被传输的数据看作是无记录边界的字节流。在TCP/IP协议簇中,使用TCP协议来实现字节流的传输,当用户想要发送大批量的数据或者对数据传输有较高的要求时,可以使用流式套接字。
2.数据报套接字(UDP)
它提供了一种无连接、不可靠的双向数据传输服务。数据包以独立的形式被发送,并且保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端按发送顺序接收数据。在TCP/IP协议簇中,使用UDP协议来实现数据报套接字。在出现差错的可能性较小或允许部分传输出错的应用场合,可以使用数据报套接字进行数据传输,这样通信的效率较高。
3.原始套接字(IP或者ICMP)
该套接字允许对较低层协议(如IP或ICMP)进行直接访问,常用于网络协议分析,检验新的网络协议实现,也可用于测试新配置或安装的网络设备。
Go Socket编程
Go提供net网络包实现基本的tcp/udp网络编程能力。使用net包非常简单。
服务端实现思路:
- 首先启动一个监听器,声明传输协议与主机端口信息;
- 启动一个无限循环不断从监听器接收客户端连接;
- 为每个客户端连接创建一个处理协程,处理协程实现对客户端的响应。
客户端实现思路:
- 首先客户端拨号连接服务端,声明传输协议与服务端主机和端口信息;
- 准备一个读写缓冲的字节数组
- 启动一个无限循环不断从终端读取用户输入,通过缓冲字节数组发送给服务端并等待服务端响应。
服务端实现 Server.go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| package main
import ( "fmt" "net" "strings" "time" )
func handlerConn(conn net.Conn) { defer conn.Close() // 拿到客户端地址 addr := conn.RemoteAddr().String() fmt.Printf("[%s] connection success\n", addr)
buf := make([]byte, 1024) for { n, err := conn.Read(buf) if err != nil { // 客户端主动关闭 if err.Error() == "EOF" { fmt.Printf("[%s] exit\n", addr) return }
fmt.Printf("[%s] conn read err:%s\n", addr, err) return } msg := string(buf[:n]) fmt.Printf("[%v] Received a message from [%s]:\n %s\n\n", time.Now().UTC(), addr, msg) if "exit" == msg { fmt.Printf("[%s] exit\n", addr) return } conn.Write([]byte(strings.ToUpper(msg))) } }
func server() { listener, err := net.Listen("tcp", ":8888") if err != nil { fmt.Println("listener err", err) return } defer listener.Close()
for { conn, err := listener.Accept() if err != nil { fmt.Println("conn client err", err) return }
go handlerConn(conn) }
}
func main() { server() }
------------------------- [2019-08-07 03:35:21.6288183 +0000 UTC] Received a message from [127.0.0.1:62777]: hello
[2019-08-07 03:35:28.3784244 +0000 UTC] Received a message from [127.0.0.1:62777]: 你好呀
[2019-08-07 03:35:35.9991891 +0000 UTC] Received a message from [127.0.0.1:62777]: exit
|
客户端实现 Client.go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package main
import ( "bufio" "fmt" "net" "os" "time" )
func received(conn net.Conn) { defer conn.Close() buf := make([]byte, 1024) addr := conn.RemoteAddr() for { n, err := conn.Read(buf) if err != nil { fmt.Println("read err:", err) continue } msg := string(buf[:n]) fmt.Printf("[%v] Received a message from [%s]:\n %s\n\n", time.Now().UTC(), addr, msg) } }
func main() { conn, err := net.Dial("tcp", "127.0.0.1:8888") if err != nil { fmt.Println("[client]: Dial err") return } defer conn.Close()
go received(conn)
scan := bufio.NewScanner(os.Stdin) for scan.Scan() { line := scan.Text() conn.Write([]byte(line)) if "exit" == line { break } }
} --------------- hello [2019-08-07 03:35:21.6395571 +0000 UTC] Received a message from [127.0.0.1:8888]: HELLO
你好呀 [2019-08-07 03:35:28.3891645 +0000 UTC] Received a message from [127.0.0.1:8888]: 你好呀
exit
|