Socket编程基本原理
Socket编程是指在网络编程中使用Socket接口进行应用程序开发的过程。Socket(套接字)是网络通信中的一个基本概念,是一个抽象的数据结构,用于描述网络中的一个端点。Socket包含了IP地址、端口号、协议类型等信息,用于标识网络中的一个特定的进程或设备。
Socket编程基于两个概念:客户端和服务器。客户端是指发起请求的一方,服务器是指提供服务的一方。通过在网络上建立连接,客户端可以向服务器发送请求,服务器则处理请求并将响应返回给客户端。
Socket编程需要使用一种特殊的协议,这种协议包含了一系列规定的数据格式和交互方式。目前主流的Socket协议有TCP和UDP。TCP协议是一种面向连接的协议,提供数据传输的可靠性。UDP协议则是一种无连接的协议,数据传输不可靠但速度快。
- TCP(传输控制协议):面向连接,可靠,有序,基于字节流。
- UDP(用户数据报协议):无连接,不可靠,无序,基于数据报。
Go 语言提供了强大的 net
包,简化了 Socket 编程,使得开发者无需直接操作底层的系统调用即可实现网络通信。
TCP Socket 编程
TCP 是一种面向连接的协议,适用于需要可靠数据传输的场景。
服务器端实现
以下是一个简单的 TCP 服务器示例,它监听指定端口,接受客户端连接,并回显接收到的消息。
package main
import (
"bufio"
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
// 读取客户端发送的数据
message, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取数据错误:", err)
break
}
fmt.Printf("收到消息: %s", message)
// 回显消息给客户端
conn.Write([]byte("Echo: " + message))
}
}
func main() {
// 监听本地 8080 端口
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("监听端口失败:", err)
return
}
defer listener.Close()
fmt.Println("服务器正在监听端口 8080...")
for {
// 接受客户端连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("接受连接失败:", err)
continue
}
// 处理连接(可以并发处理多个连接)
go handleConnection(conn)
}
}
说明:
- 使用
net.Listen
监听指定地址和端口。 - 使用
listener.Accept
接受客户端连接,返回一个net.Conn
对象。 - 为每个连接启动一个新的 goroutine 处理,实现并发处理多个客户端。
客户端实现
以下是一个简单的 TCP 客户端示例,它连接到服务器并发送消息。
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
// 连接到服务器(localhost:8080)
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("连接服务器失败:", err)
return
}
defer conn.Close()
reader := bufio.NewReader(os.Stdin)
for {
// 从标准输入读取用户输入
fmt.Print("请输入消息: ")
message, _ := reader.ReadString('\n')
// 发送消息到服务器
_, err := conn.Write([]byte(message))
if err != nil {
fmt.Println("发送消息失败:", err)
return
}
// 接收服务器回显的消息
response, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
fmt.Println("接收消息失败:", err)
return
}
fmt.Print("服务器回显: " + response)
}
}
说明:
- 使用
net.Dial
连接到服务器。 - 从标准输入读取用户输入并发送到服务器。
- 接收服务器的回显消息并显示。
UDP Socket 编程
UDP 是一种无连接的协议,适用于对实时性要求高但对可靠性要求不高的场景。下面将分别介绍如何使用 Go 实现 UDP 服务器和客户端。
服务器端实现
以下是一个简单的 UDP 服务器示例,它监听指定端口,接收客户端发送的数据,并回显消息。
package main
import (
"fmt"
"net"
)
func main() {
// 监听本地 8081 端口的 UDP
addr, err := net.ResolveUDPAddr("udp", ":8081")
if err != nil {
fmt.Println("解析地址失败:", err)
return
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("监听 UDP 失败:", err)
return
}
defer conn.Close()
fmt.Println("UDP 服务器正在监听端口 8081...")
buffer := make([]byte, 1024)
for {
// 读取客户端发送的数据
n, clientAddr, err := conn.ReadFromUDP(buffer)
if err != nil {
fmt.Println("读取数据错误:", err)
continue
}
message := string(buffer[:n])
fmt.Printf("收到来自 %s 的消息: %s", clientAddr, message)
// 回显消息给客户端
_, err = conn.WriteToUDP([]byte("Echo: "+message), clientAddr)
if err != nil {
fmt.Println("发送消息失败:", err)
}
}
}
说明:
- 使用
net.ResolveUDPAddr
解析 UDP 地址。 - 使用
net.ListenUDP
监听 UDP 端口。 - 使用
ReadFromUDP
接收数据,并获取客户端地址。 - 使用
WriteToUDP
发送数据回客户端。
客户端实现
以下是一个简单的 UDP 客户端示例,它发送消息到服务器并接收回显。
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
// 解析服务器地址
serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8081")
if err != nil {
fmt.Println("解析服务器地址失败:", err)
return
}
// 创建一个 UDP 连接(实际上是无连接的)
conn, err := net.DialUDP("udp", nil, serverAddr)
if err != nil {
fmt.Println("连接 UDP 服务器失败:", err)
return
}
defer conn.Close()
reader := bufio.NewReader(os.Stdin)
for {
// 从标准输入读取用户输入
fmt.Print("请输入消息: ")
message, _ := reader.ReadString('\n')
// 发送消息到服务器
_, err := conn.Write([]byte(message))
if err != nil {
fmt.Println("发送消息失败:", err)
return
}
// 接收服务器回显的消息
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("接收消息失败:", err)
return
}
fmt.Print("服务器回显: " + string(buffer[:n]))
}
}
说明:
- 使用
net.ResolveUDPAddr
解析服务器地址。 - 使用
net.DialUDP
创建一个 UDP 连接(实际上是无连接的)。 - 发送和接收数据类似于 TCP 客户端。