大部分底层网络的编程都离不开socket编程。HTTP编程、Web开发、IM通信、视频流传输的底层都是socket编程。关于socket编程的基础知识参考TCP/IP协议栈的相关知识。
socket源于Unix,C语言世界中的socket编程。Python采用C语言实现,其自带的socket接口可以看作是Unix socket的OOP风格的socket编程接口。大致的过程如下:
服务器端:
- 创建套接字
- 绑定地址
- 设置监听队列
- 监听请求
- 处理请求
- 关闭套接字
客户端:
- 创建套接字
- 连接服务器
- 发送数据
- 接收数据
- 关闭套接字
但Go语言采用一套新的接口,该接口比Unix中的socket简介多。由于Golang的静态编译,使用Golang编写的socket网络工具使用起来很方便。
本文先从TCP socket编程开始,然后到UDP socket,最后以一个简单的socket工具作为结尾。
TCP Socket
Golang net包中TCPConn用来建立TCP客户端、TCP服务器端间的通信通道。类似于Python中的socket对象(通过socket.socket创建)通过DialTCP函数创建该对象。TCPListener服务器端socket,监听网络请求,通过ListenTCP函数创建该对象。形象地理解,可以把TCPConn看作是TCP的连接器;TCPListener看作是TCP监听器。监听器等待客户端连接,一旦有连接进来,创建TCP连接器,然后通过TCP连接器进行服务器端和客户端的通信。
下面通过TCP心跳(返回服务器端时间)来举例,编程中还有一些细节问题,这里不详细说,看代码注释。
TCP Server
TCP服务器端通过ListenTCP创建TCPListener对象。调用Accept方法进入LISTEN状态(参考TCP状态机)
编写tcp_tick_time.go代码。
package main
import (
"fmt"
"log"
"net"
"os"
"time"
)
func echo(conn *net.TCPConn) {
tick := time.Tick(5 * time.Second) // 五秒的心跳间隔
for now := range tick {
n, err := conn.Write([]byte(now.String()))
if err != nil {
log.Println(err)
conn.Close()
return
}
fmt.Printf("send %d bytes to %s\n", n, conn.RemoteAddr())
}
}
func main() {
address := net.TCPAddr{
IP: net.ParseIP("127.0.0.1"), // 把字符串IP地址转换为net.IP类型
Port: 8000,
}
listener, err := net.ListenTCP("tcp4", &address) // 创建TCP4服务器端监听器
if err != nil {
log.Fatal(err) // Println + os.Exit(1)
}
for {
conn, err := listener.AcceptTCP()
if err != nil {
log.Fatal(err) // 错误直接退出
}
fmt.Println("remote address:", conn.RemoteAddr())
go echo(conn)
}
}
通过终端直接启动。
go run tcp_tick_time.go
通过简单的客户端连接,我们可以看到该程序终端的显示信息:
remote address: 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
send 35 bytes to 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
send 37 bytes to 127.0.0.1:1915
2017/08/29 9:23:49 write tcp4 127.0.0.1:8000->127.0.0.1:1915: wsasend: An established connection was aborted by the software in your host machine.
send 0 bytes to 127.0.0.1:1915
TCP Client
通过func DialTCP(net string, laddr, raddr TCPAddr) (TCPConn, error)创建TCP客户端。net需要指定具体的TCP
网络类型:”tcp”, “tcp4”, “tcp6”。laddr表示本地地址,一般为nil。raddr为要连接的服务器地址。
我们写一个简单的HTTP客户端。
package main
import (
"net"
"log"
"fmt"
"io/ioutil"
"os"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s host:port", os.Args[0])
}
service := os.Args[1]
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
if err != nil {
log.Fatal(err)
}
conn, err := net.DialTCP("tcp4", nil, tcpAddr)
if err != nil {
log.Fatal(err)
}
n, err := conn.Write([]byte("HEAD / HTTP/1.1\r\n\r\n"))
if err != nil {
log.Fatal(err)
}
完。
转载请注明:爱开源 » Go语言Socket编程