Golang 主要面向后端服务编程,网络通信是服务端程序必不可少的重点。主流通信协议有 udp 和 tcp,通过编程语言封装成 socket,简化编程。主要实现对应的服务端和客户端。
- golang udp
- golang tcp
- golang kcp
Golang udp
server
import (
"fmt"
"net"
)
func main() {
laddr, err := net.ResolveUDPAddr("udp", ":9000")
if err != nil {
fmt.Println("地址解析失败!", err)
return
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
fmt.Println("udp服务启动失败!", err)
return
}
fmt.Println("udp服务启动成功")
defer conn.Close()
for {
buf := make([]byte, 65535)
num, remoteAddr, err := conn.ReadFromUDP(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
continue
}
fmt.Println("客户端地址:", remoteAddr)
fmt.Printf("接收客户端数据长度:%d, 数据:%s", num, buf)
send := []byte("receive success!")
_, err = conn.WriteToUDP(send, remoteAddr)
if err != nil {
fmt.Println("发送数据失败!", err)
continue
}
}
}
client
import (
"fmt"
"net"
)
func main() {
laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9000")
if err != nil {
fmt.Println("地址解析失败!", err)
return
}
conn, err := net.DialUDP("udp", nil, laddr)
if err != nil {
fmt.Println("连接服务失败!", err)
return
}
send := []byte("hello world!")
_, err = conn.Write(send)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
buf := make([]byte, 65535)
num, remoteAddr, err := conn.ReadFromUDP(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
return
}
fmt.Println("服务端地址:", remoteAddr)
fmt.Printf("接收服务端数据长度:%d, 数据:%s", num, buf)
}
Golang tcp
server
import (
"fmt"
"net"
)
func main() {
laddr, err := net.ResolveTCPAddr("tcp", ":9000")
if err != nil {
fmt.Println("地址解析失败!", err)
return
}
listener, err := net.ListenTCP("tcp", laddr)
if err != nil {
fmt.Println("tcp服务启动失败!", err)
return
}
fmt.Println("tcp服务启动成功")
defer listener.Close()
for {
conn, err := listener.AcceptTCP()
if err != nil {
fmt.Println("建立连接失败!", err)
continue
}
go handleTcpClient(conn)
}
}
func handleTcpClient(conn *net.TCPConn) {
buf := make([]byte, 4096)
for {
num, err := conn.Read(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
return
}
fmt.Println("客户端地址:", conn.RemoteAddr())
fmt.Printf("接收客户端数据长度:%d, 数据:%s", num, buf[:num])
if fmt.Sprintf("%s", buf[:num]) == "ByeBye" {
// 接收到 ByeBye 就断线
fmt.Println("客户端离线!")
return
}
send := []byte("receive success!")
_, err = conn.Write(send)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
}
}
client
import (
"fmt"
"net"
)
func main() {
laddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9000")
if err != nil {
fmt.Println("地址解析失败!", err)
return
}
conn, err := net.DialTCP("tcp", nil, laddr)
if err != nil {
fmt.Println("连接服务失败!", err)
return
}
send := []byte("hello world!")
buf := make([]byte, 65535)
for i := 0; i < 10; i++ {
_, err = conn.Write(send)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
num, err := conn.Read(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
return
}
fmt.Println("服务端地址:", conn.RemoteAddr())
fmt.Printf("接收服务端数据长度:%d, 数据:%s", num, buf[:num])
}
bye := []byte("ByeBye")
_, err = conn.Write(bye)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
}
Golang kcp
简介:kcp是一个基于udp实现快速、可靠、向前纠错的的协议,能以比TCP浪费10%-20%的带宽的代价,换取平均延迟降低30%-40%,且最大延迟降低三倍的传输效果。
kcp-go是用go实现了kcp协议的一个库.
server
import (
"fmt"
"github.com/xtaci/kcp-go"
)
func main() {
// 不加密,不设置
listener, err := kcp.ListenWithOptions(":9000", nil, 0, 0)
if err != nil {
fmt.Println("udp服务启动失败!", err)
return
}
fmt.Println("kcp服务启动成功")
defer listener.Close()
for {
conn, err := listener.AcceptKCP()
if err != nil {
fmt.Println("建立连接失败!", err)
continue
}
fmt.Println("客户端地址:", conn.RemoteAddr())
go handleKcpClient(conn)
}
}
func handleKcpClient(conn *kcp.UDPSession) {
buf := make([]byte, 4096)
for {
num, err := conn.Read(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
return
}
fmt.Printf("接收客户端数据长度:%d, 数据:%s\n", num, buf[:num])
if fmt.Sprintf("%s", buf[:num]) == "ByeBye" {
// 接收到 ByeBye 就断线
fmt.Println("客户端离线!")
return
}
send := []byte("receive success!")
_, err = conn.Write(send)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
}
}
client
import (
"fmt"
"github.com/xtaci/kcp-go"
"strconv"
"time"
)
var data = []byte("hello world!")
func main() {
conn, err := kcp.DialWithOptions("127.0.0.1:9000", nil, 0, 0)
if err != nil {
fmt.Println("连接服务失败!", err)
return
}
fmt.Println("服务端地址:", conn.RemoteAddr())
go handleKcpConn(conn)
select {}
}
func handleKcpConn(conn *kcp.UDPSession) {
buf := make([]byte, 65535)
for {
send := append(data, strconv.FormatInt(time.Now().Unix(), 10)...)
num, err := conn.Write(send)
if err != nil {
fmt.Println("发送数据失败!", err)
return
}
num, err = conn.Read(buf)
if err != nil {
fmt.Println("接收数据失败!", err)
return
}
fmt.Printf("接收服务端数据长度:%d, 数据:%s\n", num, buf[:num])
time.Sleep(time.Second)
}
}
kcp-go实现的 kcp 编程看上去和 tcp 很相似,但是实际底层使用的是 udp 协议。kcp 头部占24个字节,kcp 发送数据会先分包,加装 kcp 头部,再通过 udp 发送数据到远端。远端需要返回 ack,根据返回的ack来判断数据段是否需要重传还是在队列里清除该数据段。用抓包工具抓包就会发现有很多24字节的 udp 包。