手记

Golang 学习笔记——网络编程

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 包。

0人推荐
随时随地看视频
慕课网APP