go

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.Readerio.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('\n')
		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) + "\n"
		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('\n')
		// 根据格式说明符格式化数据并写入连接
		fmt.Fprintf(c, text+"\n")
		// 读取 tfp服务 数据返回
		message, _ := bufio.NewReader(c).ReadString('\n')
		fmt.Print("->: " + message)
		// 如果服务端返回 STOP ,退出for循环
		if strings.TrimSpace(string(text)) == "STOP" {
			fmt.Println("TCP client exiting...")
			return
		}
	}
}

image-20210930203158587

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('\n')
		// 将数据加上回车符,转为字节数组
		data := []byte(text+"\n")
		// 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\n", 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\n", string(data))
		// udpConn返回data给udp客户端
		_, err = udpConn.WriteToUDP(data, addr)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
}

image-20210930211149930

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

posted @ 2021-09-30 21:21  临渊不羡渔  阅读(126)  评论(0编辑  收藏  举报