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
,指本地机,一般用于测试使用
- A 类:保留给政府结构,
-
端口号:
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)
}
}