Go语言标准库里提供的net包,支持基于IP层、TCP/UDP层及更高层面(如HTTP、FTP、SMTP)的网络操作,其中用于IP层的称为Raw Socket。
net.Dial()
函数对TCP和UDP等协议的建立、绑定、监听socket作了抽象和封装,只需要把协议和地址作为参数传递给它,即可处理。Dial()
返回的Conn接口,支持Io.Reader和io.Writer等。
一、TCP 服务端客户端
根据协议Echo Protocol RFC 862,使用GO编写一个简单Echo服务。
package main
import (
"fmt"
"net"
"os"
"io"
)
const (
CONN_HOST = "localhost"
CONN_PORT = "3333"
CONN_TYPE = "tcp"
)
func main() {
// 1. 监听地址
l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer l.Close()
fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
for {
// 2. 接受连接
conn, err := l.Accept()
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
fmt.Printf("Received message %s -> %s \n", conn.RemoteAddr(), conn.LocalAddr())
// 在一个Goroutine中处理conn
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
defer conn.Close()
for {
io.Copy(conn, conn)
}
}
启动main函数后,通过telnet 客户端即可访问:
telnet localhost 3333
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello server
hello server
如果是go编写客户端,我们希望通过两个goroutine,一个写数据到服务端,一个读取相应,如下通过sync完成:
package main
import (
"net"
"fmt"
"os"
"sync"
"strconv"
"bufio"
)
const (
HOST = "localhost"
PORT = "3333"
TYPE = "tcp"
)
func main() {
conn, err := net.Dial(TYPE, HOST+":"+PORT)
if err != nil {
fmt.Println("Error connecting:", err)
os.Exit(1)
}
defer conn.Close()
var wg sync.WaitGroup
wg.Add(2)
fmt.Println("Connecting to " + HOST + ":" + PORT)
go writeData(conn, &wg)
go getResponse(conn, &wg)
wg.Wait()
}
func writeData(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
for i := 10; i > 0; i-- {
_, e := conn.Write([]byte("hello " + strconv.Itoa(i) + "\r\n"))
if e != nil {
fmt.Println("Error to send message because of ", e.Error())
break
}
}
}
func getResponse(conn net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
reader := bufio.NewReader(conn)
for i := 1; i <= 10; i++ {
line, err := reader.ReadString(byte('\n'))
if err != nil {
fmt.Print("Error to read message because of ", err)
return
}
fmt.Print(line)
}
}
当然,也可以使用channel。
二、UDP 服务端
服务端简单实现:
package main
import (
"fmt"
"net"
"os"
)
func main() {
// 1. 监听4444
udp_addr, err := net.ResolveUDPAddr("udp", ":4444")
checkError(err)
//2. 创建监听连接
conn, err := net.ListenUDP("udp", udp_addr)
defer conn.Close()
checkError(err)
//3. 处理连接
recvUDPMsg(conn)
}
func recvUDPMsg(conn *net.UDPConn) {
data := make([]byte, 1024)
n, addr, err := conn.ReadFromUDP(data)
if err != nil {
return
}
fmt.Println("udp msg: ", string(data[:n]))
_, err = conn.WriteToUDP([]byte("nice to see u"), addr)
checkError(err)
}
func checkError(err error) {
if err != nil {
fmt.Printf("Error:%s", err.Error())
os.Exit(1)
}
}
可以使用nc -vuz localhost 4444
测试,代码实现也很简单:
conn, err := net.Dial("udp", "host:port")
引用列表: