zoukankan      html  css  js  c++  java
  • 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('
    ')
    		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
    		}
    	}
    }
    
    

    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('
    ')
    		// 将数据加上回车符,转为字节数组
    		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
    		}
    	}
    }
    
    

    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

  • 相关阅读:
    【Java多线程系列四】控制线程执行顺序
    【Java多线程系列随笔二】BlockingQueue
    【Java多线程系列三】实现线程同步的方法
    【Java多线程系列随笔一】浅析 Java Thread.join()
    【Java多线程系列二】Thread类的方法
    【Java多线程系列一】Java实现线程方法
    如何优雅的使用和理解线程池
    spring 中 isolation 和 propagation 详解
    嵊州普及Day6T1
    嵊州普及Day5T4
  • 原文地址:https://www.cnblogs.com/jiangdewen/p/15358134.html
Copyright © 2011-2022 走看看