Go语言Socket编程教程
协议 | 定义 |
---|---|
TCP(传输控制协议) | TCP 的主要特征是它在设计上是一种可靠的协议。如果没有数据包交付的证据,TCP 将重新发送数据包。TCP 数据包可用于的一些任务是建立连接、传输数据、发送确认和关闭连接。 |
IP(互联网协议) | IP 协议遵循端到端原则,将所有网络智能置于端节点而不是中间节点。这种设计有利于降低网络复杂性而不是可靠性。出于这个原因,互联网协议不保证数据包在网络上的可靠传递。相反,IP 与 TCP 一起工作以通过网络可靠地传送数据包。 |
UDP(用户数据报协议): | UDP 提供了一种更简单的传输层协议实现,虽然不如 TCP 可靠,但速度要快得多。UDP 不提供错误检查、纠正或数据包重传,因此速度非常快。当速度比可靠性更重要时,通常选择 UDP 而不是 TCP。UDP 通常用于在线游戏、视频聊天和其他实时应用程序。 |
net
包
Go的
net
包为网络I/O提供了一个可移植的接口,包括TCP/IP、UDP、域名解析和Unix域套接字。在本指南中,您将使用此软件包创建TCP和UDP服务器以及客户端。
net.Dial()
和net.Listen()
返回的数据类型是实现了io.Reader
和io.Writer
接口的。所以我们可以像使用常规文件I/O函数操作一样从TCP/IP连接发送和接收数据。
相应函数
返回值 | 函数 |
---|---|
Listener | func Listen(network, address string) (Listener, error) • network 取值范围是{tcp , tcp4 (IPv4-only), tcp6 (IPv6-only), unix (Unix sockets),unixpacket }• address 定义服务器将侦听的服务器地址和端口号 |
UDPConn | func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error) • 该方法用于创建UDP服务 • network 取值范围:{"udp", "udp4", "udp6"}• laddr 参数定义了服务器将侦听的服务器地址和端口号func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error) • 该方法用于创建UDP客户端 • network 取值范围:{"udp", "udp4", "udp6"}• laddr 是监听地址(服务器)。如果laddr 为 nil,则自动选择本地地址。• raddr 是响应地址(客户端)。如果raddr 的 IP 为 nil 或是未指定的 IP 地址,则取127.0.0.1。 |
UDPAddr | func ResolveUDPAddr(network, address string) (*UDPAddr, error) • 这个函数返回一个 UDP 端口定义指针• network 取值范围:{"udp", "udp4", "udp6"}• address 参数格式为host:port . 这个host是一个ip地址, 或者是一个可解析的域名。例如localhost |
TCPAddr | func ResolveTCPAddr(network, address string) (*TCPAddr, error) • 这个函数返回一个 TCP 端口定义指针• network 取值范围:{"tcp", "tcp4", "tcp6"}• address 参数格式为host:port . 这个host是一个ip地址, 或者是一个可解析的域名。例如localhost |
Conn | func Dial(network, address string) (Conn, error) • 返回一个使用指定网络协议连接到指定地址的连接 • network 取值范围:{"tcp","tcp4","tcp6","udp","udp4","udp6","ip","ip4","ip6","unix","unixgram","unixpacket"}• 当使用 TCP 或者ICP 网络协议时, address 参数格式为host:port . 这个host是一个ip地址, 或者是一个可解析的域名。例如localhost |
TCPConn | func ListenTCP(network string, laddr *TCPAddr) (*TCPConn, error) • 该方法用于创建TCP服务 • network 取值范围:{"tcp", "tcp4", "tcp6"}• laddr 参数定义了服务器将侦听的服务器地址和端口号func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) • 这个函数返回一个 TCP 连接指针• network 取值范围:{"tcp", "tcp4", "tcp6"}• laddr 是监听地址(服务器)。如果为 nil,则取默认值• raddr 是响应地址(客户端). 如果raddr 的 IP 为 nil 或是未指定的 IP 地址,则取127.0.0.1。 |
TCP demo
TCP 服务端
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
"time"
)
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide port number")
return
}
PORT := ":" + arguments[1]
// tcp监听端口,返回一个监听指定端口的 监听
l, err := net.Listen("tcp", PORT)
if err != nil {
fmt.Println(err)
return
}
defer l.Close()
// 阻塞,接入连接时,返回一个 监听指定端口 的 连接。
c, err := l.Accept()
if err != nil {
fmt.Println(err)
return
}
for {
// 从连接中取数据,边界符号是回车
netData, err := bufio.NewReader(c).ReadString('
')
if err != nil {
// 从连接中获取数据出错,打印异常,退出for循环,触发 l.close()方法,关闭TCP监听
fmt.Println(err)
return
}
if strings.TrimSpace(string(netData)) == "STOP" {
// 接收到 STOP 消息,退出for循环,触发 l.close()方法,关闭TCP监听
fmt.Println("Exiting TCP server!")
return
}
// 打印接收到的数据
fmt.Print("-> ", string(netData))
// 获取当前时间,并将其格式化,写入连接返回给客户端
t := time.Now()
myTime := t.Format(time.RFC3339) + "
"
c.Write([]byte(myTime))
}
}
TCP客户端
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
args := os.Args
if len(args) == 1 {
fmt.Println("Please provide host:port")
return
}
CONNECT := args[1]
// 使用 tcp 协议连接到指定地址
c, err := net.Dial("tcp", CONNECT)
if err != nil {
fmt.Println(err)
return
}
for {
// 获取指令台输入
reader := bufio.NewReader(os.Stdin)
fmt.Println(">>>")
// 获取数据,知道遇到回车符
text, _ := reader.ReadString('
')
// 根据格式说明符格式化数据并写入连接
fmt.Fprintf(c, text+"
")
// 读取 tfp服务 数据返回
message, _ := bufio.NewReader(c).ReadString('
')
fmt.Print("->: " + message)
// 如果服务端返回 STOP ,退出for循环
if strings.TrimSpace(string(text)) == "STOP" {
fmt.Println("TCP client exiting...")
return
}
}
}
UDP demo
UDP服务端
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
args := os.Args
if len(args) == 1 {
fmt.Println("Please provide a host:port string")
return
}
CONNECT := args[1]
udpAddr, _ := net.ResolveUDPAddr("udp4", CONNECT)
UDPConn, _ := net.DialUDP("udp4", nil, udpAddr)
fmt.Printf("The UDP server is %sn", UDPConn.RemoteAddr().String())
defer UDPConn.Close()
for {
reader := bufio.NewReader(os.Stdin)
fmt.Printf(">>>")
// 以回车符作为分隔符获取数据
text, _ := reader.ReadString('
')
// 将数据加上回车符,转为字节数组
data := []byte(text+"
")
// udp连接写入数据
_, err := UDPConn.Write(data)
if strings.TrimSpace(string(data)) == "STOP" {
fmt.Println("Exiting UDP client!")
return
}
if err != nil {
fmt.Println(err)
return
}
buffer := make([]byte, 1024)
n, _, err := UDPConn.ReadFromUDP(buffer)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Reply: %s
", string(buffer[0:n]))
}
}
UDP客户端
package main
import (
"fmt"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
)
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide a port number!")
return
}
PORT := ":" + arguments[1]
// 以 udp4 协议,解析获得udp地址
udpAddr, err := net.ResolveUDPAddr("udp4", PORT)
if err != nil {
fmt.Println(err)
return
}
// 以 udp4 协议,监听udp地址,返回udp连接
udpConn, err := net.ListenUDP("udp4", udpAddr)
if err != nil {
fmt.Println(err)
return
}
defer udpConn.Close()
buffer := make([]byte, 1024)
rand.Seed(time.Now().Unix())
for {
// 从udp连接中读取数据至缓存数组,并打印
n, addr, err := udpConn.ReadFromUDP(buffer)
fmt.Print("->", string(buffer[0:n-1]))
// 如果客户端发送 STOP 消息,退出for循环,触发udpConn.Close(),关闭udp连接
if strings.TrimSpace(string(buffer[0:n])) == "STOP" {
fmt.Println("Exiting UDP server!")
return
}
// 获取1到10000的随机数格式化为字符串,构建返回数据
data := []byte(strconv.Itoa(rand.Intn(1000) + 1))
fmt.Printf("data: %s
", string(data))
// udpConn返回data给udp客户端
_, err = udpConn.WriteToUDP(data, addr)
if err != nil {
fmt.Println(err)
return
}
}
}
UDP极简demo
为语法简洁省略错误判断与处理
udp-server
package main
import (
"fmt"
"net"
)
func main() {
ServerConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP: []byte{0, 0, 0, 0}, Port: 10001, Zone: ""})
defer ServerConn.Close()
buf := make([]byte, 1024)
for {
n, addr, _ := ServerConn.ReadFromUDP(buf)
fmt.Println("Received ", string(buf[0:n]), " from ", addr)
}
}
udp-client
package main
import "net"
func main() {
Conn, _ := net.DialUDP("udp", nil, &net.UDPAddr{IP:[]byte{127,0,0,1},Port:10001,Zone:""})
defer Conn.Close()
Conn.Write([]byte("hello"))
}
参考地址:
https://www.xpresservers.com/create-a-tcp-and-udp-client-and-server-using-go
https://haicoder.net/golang/golang-network.html