osi参考模型将计算机网络结构分为7个层次,但是在实际的开发应用中,我们更加认可TCP/IP族协议的五层结构,即应用层(http、ftp、dns),传输层(udp、tcp),网络层(ip),链路层(以太网),物理层。
socket编程作为一种基于网络层和传输层的数据io模式主要分为两种,TCP Socket和UDP Socket,也即面向连接的流式Socket和面向无连接的数据报式Socket。
今天主要和大家讲讲golang的TCP Socket编程。先来看个简单的例子吧
server.go
//模拟server端
func main() {
tcpServer, _ := net.ResolveTCPAddr("tcp4", ":8080")
listener, _ := net.ListenTCP("tcp", tcpServer)
for {
//当有新的客户端请求来的时候,拿到与客户端的连接
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
continue
}
//处理逻辑
go handle(conn)
}
}
func handle(conn net.Conn) {
defer conn.Close()
//读取客户端传送的消息
go func() {
response, _ := ioutil.ReadAll(conn)
fmt.Println(string(response))
}()
//向客户端发送消息
time.Sleep(1 * time.Second)
now := time.Now().String()
conn.Write([]byte(now))
}
client.go
//模拟客户端
func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])
}
//获取命令行参数 socket地址
server := os.Args[1]
addr, err := net.ResolveTCPAddr("tcp4", server)
checkError(err)
//建立tcp连接
conn, err := net.DialTCP("tcp4", nil, addr)
checkError(err)
//向服务端发送数据
_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
checkError(err)
//接收响应
response, _ := ioutil.ReadAll(conn)
fmt.Println(string(response))
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
整体大概的流程是:
server端先通过tcp协议监听端口8080,之后当有客户端来访问时能够读取信息并且写入响应信息,注意此处 conn, err := listener.Accept() 语句是用来获取下一个调用连接的,如果一直没有连接,则会处于阻塞状态。
客户端通过DialTCP尝试与服务端建立连接,连接成功后向服务器端发送数据并接收响应,并且以上代码中,只有等待服务器端关闭连接之后,该连接才会失效,否则连接会一直处于ESTABLISHED状态。如下图是笔者将defer conn.Close()注释掉之后,查看的8080端口的连接,会发现连接一直存在: