Golang Sockek编程

网络基本概念

  • 网络编程的目的:直接或间接地通过网络协议与其他计算机进行通讯
  • 网络编程中两个主要问题:
  • 如何准确定位网络上一台或多台主机(通过 IP 地址)
  • 找到主机后如何进行数据传输(有 OSI 模型和 TCP/IP 模型)
  • OSI 模型将网络分为 7 层,过于理想化,未能广泛推广
  • TCP/IP 是事实上的国际标准
OSI TCP/IP TCP/IP 对应的协议
应用层 应用层 HTTP、FTP、DNS
表示层
会话层
传输层 传输层 TCP、UDP
网络层 网络层 IP、ARP、ICMP
数据链路层 物理数据层 Link
物理层
  • IP 分类:最开始是 32 位整数,后来用圆点隔开,分成 4 段,8 位一段

    • A 类:保留给政府结构,1.0.0.1 ~ 126.255.255.254
    • B 类:分配给中型企业,1.0.0.1 ~ 126.255.255.254
    • C 类:分配给任何需要的个人,192.0.0.1 ~ 223.255.255.254
    • D 类:用于组播,224.0.0.1 ~ 239.255.255.254
    • E 类:用于实验,240.0.0.1 ~ 255.255.255.254
    • 回环地址:127.0.0.1,指本地机,一般用于测试使用
  • 端口号:0~65535

什么是 socket

  • Socket 又称套接字,应用程序通常通过“套接字”向网络发出请求或者应答网络请求
  • 常用的 Socket 类型有两种:流式 Socket 和数据报式 Socket,流式是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用,数据报式 Socket 是一种无连接的 Socket,针对于无连接的 UDP 服务应用
  • TCP:比较靠谱,面向连接,比较慢
  • UDP:不是太靠谱,比较快

TCP 编程

  • 实现服务端与客户端通信,保持连接,服务器接收到 exit 时再断开连接

服务端

package main

import (
	"fmt"
	"io"
	"net"
	"strings"
)

// 处理客户端数据
func clientHandle(conn net.Conn) {
	// 关闭客户端连接
	defer conn.Close()
	// 获取客户端地址
	clientAddr := conn.RemoteAddr()
	fmt.Printf("%v 已连接\n", clientAddr)

	// 创建读去数据的缓冲区
	buf := make([]byte, 1024)
	for {
		// 读取数据
		// n是读取到的长度
		n, err := conn.Read(buf)
		if err == io.EOF { // 读取完毕
			break
		}
		if err != nil { // 读取失败
			fmt.Println("读取客户端数据失败, err: ", err)
			return
		}

		// 取出接收到的数据
		readResult := string(buf[:n])
		// 判断是否为退出指令
		if readResult == "exit" {
			fmt.Printf("%d退出连接", clientAddr)
			return
		}
		fmt.Printf("接收到来自[%v]数据: %s", clientAddr, readResult)
		// 回复客户端
		_, err = conn.Write([]byte(strings.ToUpper(readResult)))
		if err != nil {
			fmt.Printf("给 %v 发送数据失败: %v", clientAddr, err)
			return
		}
	}
}

func main() {
	// 1. 创建服务端监听
	listener, err := net.Listen("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println("启动服务端失败, err: ", err)
		return
	}
	// 关闭服务端资源
	defer listener.Close()

	fmt.Println("服务端启动成功")

	// 循环监听服务端
	for {
		// 堵塞监听
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("堵塞监听失败, err: ", err)
			continue
		}
		// 启动协程去处理客户端请求
		go clientHandle(conn)
	}
}

客户端

package main

import (
	"fmt"
	"net"
)

func main() {
	// 创建连接
	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println("创建连接失败, err: ", err)
		return
	}
	defer conn.Close()
	// 创建缓冲区
	buf := make([]byte, 1024)

	for {
		fmt.Printf("请输入要发送的内容: ")
		_, err = fmt.Scan(&buf)
		if err != nil {
			fmt.Println("接收输入有误, err: ", err)
			continue
		}
		fmt.Printf("发送内容[%s]到服务端\n", string(buf))

		// 发送数据
		_, err = conn.Write(buf)
		if err != nil {
			fmt.Println("发送数据失败")
			continue
		}

		// 读取数据
		rn, err := conn.Read(buf)
		if err != nil {
			fmt.Println("读取数据失败")
			continue
		}
		fmt.Printf("接收到数据: %s\n", string(buf[:rn]))
	}
}

UDP 编程

  • 编写客户端发送信息给服务,服务端返回信息,返回完切断

服务端

package main

import (
	"fmt"
	"net"
)

func main() {
	// 创建udp监听
	conn, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 8080,
	})
	if err != nil {
		fmt.Println("创建监听失败, err: ", err)
		return
	}
	defer conn.Close()

	for {
		// 创建缓冲区
		buf := make([]byte, 1024)

		// 接收UDP传输
		n, addr, err := conn.ReadFromUDP(buf)
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Printf("来自: %s 接收到数据: %s\n", addr, string(buf[:n]))
		// 返回信息
		_, err = conn.WriteToUDP([]byte("666"), addr)
		if err != nil {
			fmt.Println("err")
			continue
		}
	}
}

客户端

package main

import (
	"fmt"
	"net"
	"time"
)

func main() {
	// 连接服务端
	conn, err := net.DialUDP("udp4", nil, &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 8080,
	})
	if err != nil {
		fmt.Println("连接udp服务失败")
	}
	defer conn.Close()

	for {
		// 发送数据到服务端
		_, err = conn.Write([]byte("老铁"))
		if err != nil {
			fmt.Println("发送数据失败, err: ", err)
			return
		}

		buf := make([]byte, 1024)
		n, addr, err := conn.ReadFromUDP(buf)
		if err != nil {
			fmt.Println("接收服务端数据失败, err: ", err)
			return
		}
		fmt.Printf("服务端: %v, 数据: %s\n", addr, string(buf[:n]))
		time.Sleep(time.Second)
	}
}
posted @ 2020-03-17 13:27  ZhiChao&  阅读(421)  评论(0编辑  收藏  举报