犀牛云 做网站,天津建设工程交易中心网站,怎么注册企业邮箱免费注册,流量对于网站盈利网络编程Go语言网络编程相关APIGo语言网络编程架构Go语言的网络编程实现基于以下几个关键原理#xff1a;bufiobufio 包的主要功能和使用场景主要类型示例 tcp通信解决粘包粘包和拆包的产生原因解决方法示例 网络编程
Go语言网络编程相关API
1.1 net包net.Listen(network, a…网络编程Go语言网络编程相关APIGo语言网络编程架构Go语言的网络编程实现基于以下几个关键原理bufiobufio 包的主要功能和使用场景主要类型示例 tcp通信解决粘包粘包和拆包的产生原因解决方法示例 网络编程
Go语言网络编程相关API
1.1 net包net.Listen(network, address string): 创建一个网络监听器等待进入的连接。它的第一个参数是网络类型如tcp、udp等第二个参数是要监听的地址。net.Dial(network, address string): 连接到指定的地址返回一个连接对象。net.Conn接口: 提供了与网络连接相关的方法包括Read(b []byte) (n int, err error): 从连接中读取数据。Write(b []byte) (n int, err error): 向连接写入数据。Close() error: 关闭连接。net.Listener接口: 用于接受传入连接的接口包含方法Accept() (Conn, error): 接受一个连接请求。1.2 net/http包http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): 注册处理函数当请求的URL匹配指定模式时执行该处理函数。http.ListenAndServe(addr string, handler Handler) error: 启动HTTP服务器并监听指定地址上的请求。http.Request结构体: 包含请求的各种信息如方法、URL、头部信息等。http.ResponseWriter接口: 用于构造HTTP响应包含方法来设置响应头和写入响应体。http.Get(url string): 发起一个GET请求并返回响应。Go语言网络编程架构
Go的网络编程架构主要基于事件驱动和goroutine的并发模型。
每当一个新的连接被接受时服务器会为其启动一个新的goroutine来处理该连接。
因此Go语言能够轻松地处理大量的并发连接而无需使用复杂的线程管理。
服务器架构
监听端口服务器通过net.Listen监听指定端口。
接受连接使用listener.Accept()接收连接。
处理连接为每个连接创建goroutine执行自定义的处理逻辑。
关闭连接在处理完成后关闭连接释放资源。客户端架构
创建连接通过net.Dial或http.Get创建连接。
发送请求向服务器发送请求或数据。
接收响应获取服务器的响应数据。
关闭连接处理完成后关闭连接。Go语言的网络编程实现基于以下几个关键原理
Goroutine和通道 Go的并发模型建立在轻量级goroutine之上。 每一个网络连接都可以在单独的goroutine中处理这样可以有效利用系统资源提高并发处理能力。 通道channel用于在多个goroutine之间传递数据保证数据的安全和同步。
I/O多路复用 Go使用操作系统的I/O多路复用机制如epollLinux或kqueueBSD 来处理大量连接的读写事件从而减少线程上下文切换的开销。
封装性和易用性 Go的net和net/http包提供了高层次的抽象简化了网络编程的复杂性。 开发者不需要深入底层的socket编程只需调用简单的API即可实现复杂的网络操作。
bufio
bufio 是 Go 语言标准库中的一个包用于提供 buffered I/O缓冲输入/输出功能。
它通过对输入和输出操作进行缓冲处理从而提高程序的性能
减少系统调用的次数。bufio 常用于处理文件、网络连接等 I/O 操作。
bufio 包的主要功能和使用场景
提高效率
bufio 使用内存中的缓冲区来存储数据从而减少直接的系统调用次数如读写磁盘或网络提高了读写效率。
简单的接口
bufio 提供了简单易用的接口让开发者能够更方便地处理文本和二进制数据。
处理文本数据
bufio 特别适合处理行或单词输入输出可以方便地读取和写入文本数据。
主要类型
以下是 bufio 包中几个重要的类型
Readerbufio.Reader 是一个结构体用于缓存输入。通过 Read() 方法可以从底层的 io.Reader 中读取数据使用缓冲可以减少调用次数。
常用方法Read(p []byte) (n int, err error): 从缓冲区读取数据到切片 p 中。ReadString(delim byte) (string, error): 读取直到遇到分隔符 delim 的数据并返回作为字符串。ReadBytes(delim byte) ([]byte, error): 与 ReadString 类似但返回字节切片。示例reader : bufio.NewReader(conn)message, err : reader.ReadString(\n) // 读取一行数据Writerbufio.Writer 是一个结构体用于缓存输出。可以将数据写入缓冲区并在缓冲区填满后再一次性写入到底层的 io.Writer。
常用方法Write(p []byte) (n int, err error): 将数据写入缓冲区。Flush() error: 将缓冲区中的数据写入到底层的 io.Writer。
示例writer : bufio.NewWriter(conn)writer.Write([]byte(Hello, Client!\n)) // 写入数据到缓冲区writer.Flush() // 确保将数据写入连接Scannerbufio.Scanner 提供了一个方便的方式来逐行读取输入常用于处理文本数据。
常用方法Scan() bool: 读取下一个 token返回 true 表示成功。Text() string: 返回上一个 token 作为字符串。
示例scanner : bufio.NewScanner(os.Stdin)for scanner.Scan() {line : scanner.Text() // 得到输入的一行fmt.Println(line)} 示例 tcp通信
这段代码实现了一个简单的聊天服务器能够接收并广播消息到所有连接的客户端。
使用了 goroutines 来处理每个客户端的连接
确保服务器能够同时处理多个连接。
通过 sync.Mutex 来保护对共享数据结构 clients 的并发访问。package mainimport (bufio // 导入 bufio 包用于读取输入fmt // 导入 fmt 包用于格式化输出net // 导入 net 包用于网络操作sync // 导入 sync 包用于同步操作
)// 定义全局变量
var (// 保存所有连接的客户端使用 map 结构clients make(map[net.Conn]bool)clientsMux sync.Mutex // 保护 clients 的并发访问防止数据竞态
)func main() {// 监听指定的端口8080listener, err : net.Listen(tcp, :8080)if err ! nil {// 如果监听失败打印错误信息并返回fmt.Println(Error starting server:, err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println(Chat server started on :8080) // 服务器启动成功提示for {// 接受新的连接conn, err : listener.Accept()if err ! nil {// 如果接受连接失败打印错误信息并继续下一次循环fmt.Println(Error accepting connection:, err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()// 创建一个读取器reader : bufio.NewReader(conn)for {// 读取客户端发送的消息直到换行符message, err : reader.ReadString(\n)if err ! nil {// 如果读取失败打印错误信息并返回fmt.Println(Error reading from connection:, err)return}// 打印接收到的消息fmt.Printf(Received: %s, message)// 广播消息到所有其他客户端broadcastMessage(message, conn) // 调用广播函数}
}// 广播消息给所有连接的客户端
func broadcastMessage(message string, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 遍历所有连接的客户端for client : range clients {// 不向发送消息的客户端发送消息if client ! sender {_, _ client.Write([]byte(message)) // 发送消息}}
}
这段代码实现了一个简单的聊天客户端能够连接到服务器并发送消息同时也会接收并显示服务器发来的消息。
通过 goroutines 来异步处理接收消息和发送消息确保用户可以一边发送消息一边接收来自服务器的消息。
使用 bufio.Scanner 和 bufio.Reader 来处理输入和输出的读取。
package mainimport (bufio // 导入 bufio 包用于读取输入fmt // 导入 fmt 包用于格式化输出net // 导入 net 包用于网络操作os // 导入 os 包用于与操作系统交互
)func main() {// 连接到聊天服务器指定服务器地址localhost:8080conn, err : net.Dial(tcp, localhost:8080)if err ! nil {// 如果连接失败打印错误信息并返回fmt.Println(Error connecting to server:, err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息
func readMessages(conn net.Conn) {reader : bufio.NewReader(conn) // 创建一个读取器for {// 读取服务器发送的消息直到换行符message, err : reader.ReadString(\n)if err ! nil {// 如果读取失败打印错误信息并返回fmt.Println(Error reading from server:, err)return}// 打印接收到的消息fmt.Print(Received: , message)}
}// 发送消息到服务器
func sendMessages(conn net.Conn) {scanner : bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println(消息发送中:) // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message : scanner.Text()// 发送消息到服务器并在结尾加上换行符_, err : conn.Write([]byte(message \n))if err ! nil {// 如果发送失败打印错误信息并返回fmt.Println(Error sending message:, err)return}}
}
解决粘包
在网络编程中尤其是使用TCP进行通信时常会遇到“粘包”与“拆包”现象。这是因为TCP是一个流式协议
数据在传输过程中可能会被合并成一个大的数据包或者一个大的数据包可能被分割成多个小的数据包从而导致接收方无法正确解析消息。
粘包和拆包的产生原因
粘包发送方可能连续发送多个消息而TCP将这些消息合并为一个包接收方收到的数据中包含多个消息。
拆包发送方发送一个较大的消息TCP将其分成多个小包发送接收方在一次读取中只获取到部分消息。
解决方法
为了防止粘包和拆包现象通常采用以下几种策略来处理 使用固定长度的消息 在这种方法中每个消息都使用固定的字节长度来进行编码。这样接收方可以根据固定的长度来读取数据。 优点实现简单。 缺点不适用于消息长度不一致的情形浪费带宽如果消息较短。 使用分隔符 在每个消息的末尾加入一个特定的分隔符比如换行符、特定字符等接收方在读取数据时可以根据分隔符进行解析。 优点可以处理可变长度的消息。 缺点需要确保分隔符不会出现在消息内容中。 消息头部长度 在每个消息前添加一个固定大小的头部头部包含消息的长度信息接收方首先读取头部获取消息长度再按长度读取数据。 优点适用于任意长度的消息。 缺点稍显复杂需处理头部。
示例
此代码实现了一个聊天服务器能够接受多个客户端连接
接收消息并将其广播给所有连接的客户端。
它通过使用二进制数据处理来确保消息的完整性和准确性使用协程来并发处理每个连接
确保服务器在处理多个客户端时的高效性。
通过使用 sync.Mutex 来避免对共享数据结构 clients 的竞争访问。
package mainimport (encoding/binary // 导入 encoding/binary 包用于在网络中进行字节序转换fmt // 导入 fmt 包用于格式化输出net // 导入 net 包用于网络操作sync // 导入 sync 包用于同步操作
)// 定义全局变量
var (clients make(map[net.Conn]bool) // 用于保存所有连接的客户端clientsMux sync.Mutex // 保护 clients 的并发访问
)func main() {// 监听指定的端口8080listener, err : net.Listen(tcp, :8080)if err ! nil {// 如果启动失败打印错误信息并返回fmt.Println(Error starting server:, err)return}defer listener.Close() // 在 main 函数结束时关闭监听器fmt.Println(Chat server started on :8080) // 服务器启动成功提示for {// 接受新的连接conn, err : listener.Accept()if err ! nil {// 如果接受连接失败打印错误信息并继续下一次循环fmt.Println(Error accepting connection:, err)continue}// 将新客户端添加到客户端列表clientsMux.Lock() // 加锁以保护 clients 的并发访问clients[conn] true // 将连接添加到 clientsclientsMux.Unlock() // 解锁// 启动一个 goroutine 来处理该连接go handleConnection(conn)}
}// 处理连接的函数
func handleConnection(conn net.Conn) {defer func() {// 关闭连接并从 clients 中移除conn.Close() // 关闭连接clientsMux.Lock() // 加锁以保护 clientsdelete(clients, conn) // 从 clients 中移除该连接clientsMux.Unlock() // 解锁}()for {// 读取消息长度前4个字节lengthBuffer : make([]byte, 4) // 创建一个4字节的缓冲区_, err : conn.Read(lengthBuffer) // 从连接中读取消息长度if err ! nil {// 如果读取长度失败打印错误信息并返回fmt.Println(Error reading length:, err)return}// 根据读取到的长度转换成整型msgLength : int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer : make([]byte, msgLength) // 根据长度创建消息缓冲区// 读取消息/* param messageBuffer 接收消息的缓冲区 param conn 客户端连接 return 返回读取的字节数和错误信息*/_, err conn.Read(messageBuffer) // 从连接中读取消息if err ! nil {// 如果读取消息失败打印错误信息并返回fmt.Println(Error reading message:, err)return}// 打印接收到的消息fmt.Printf(Received message: %s\n, string(messageBuffer))// 将接收到的消息广播给所有其他客户端broadcastMessage(messageBuffer, conn)}
}// 广播消息给所有连接的客户端
func broadcastMessage(message []byte, sender net.Conn) {clientsMux.Lock() // 加锁以保护 clients 的并发访问defer clientsMux.Unlock() // 在函数结束时解锁// 获取消息长度msgLength : uint32(len(message))lengthBuffer : make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 遍历所有连接的客户端for client : range clients {// 不向发送者发送消息if client ! sender {// 首先发送消息长度_, _ client.Write(lengthBuffer) // 发送长度_, _ client.Write(message) // 然后发送消息}}
}
此代码实现了一个简单的聊天客户端能够连接到服务器、发送消息并接收服务器的消息。 通过二进制传输消息长度确保了消息的完整性。 package mainimport (bufio // 导入 bufio 包用于读取输入encoding/binary // 导入 encoding/binary 包用于字节序转换fmt // 导入 fmt 包用于格式化输出net // 导入 net 包用于网络操作os // 导入 os 包用于与操作系统交互
)func main() {// 连接到聊天服务器指定服务器地址localhost:8080conn, err : net.Dial(tcp, localhost:8080)if err ! nil {// 如果连接失败打印错误信息并返回fmt.Println(Error connecting to server:, err)return}defer conn.Close() // 在 main 函数结束时关闭连接// 启动一个 goroutine 来读取服务器的消息go readMessages(conn)// 从标准输入读取消息并发送给服务器sendMessages(conn)
}// 从连接中读取消息的函数
func readMessages(conn net.Conn) {for {// 创建一个4字节的缓冲区用于读取消息长度lengthBuffer : make([]byte, 4)_, err : conn.Read(lengthBuffer) // 从连接中读取消息长度if err ! nil {// 如果读取长度失败打印错误信息并返回fmt.Println(Error reading length:, err)return}// 将读取到的长度转换为整型msgLength : int(binary.BigEndian.Uint32(lengthBuffer))messageBuffer : make([]byte, msgLength) // 根据长度创建消息缓冲区_, err conn.Read(messageBuffer) // 从连接中读取消息if err ! nil {// 如果读取消息失败打印错误信息并返回fmt.Println(Error reading message:, err)return}// 打印接收到的消息fmt.Printf(Received message: %s\n, string(messageBuffer))}
}// 发送消息到服务器的函数
func sendMessages(conn net.Conn) {scanner : bufio.NewScanner(os.Stdin) // 创建一个扫描器来读取标准输入fmt.Println(Type your messages below (end with Enter):) // 提示用户开始输入消息for scanner.Scan() {// 从输入中读取一行消息message : scanner.Text()// 获取消息长度msgLength : uint32(len(message))lengthBuffer : make([]byte, 4) // 创建一个4字节的缓冲区binary.BigEndian.PutUint32(lengthBuffer, msgLength) // 将消息长度转换为大端字节序// 先发送消息长度_, err : conn.Write(lengthBuffer) // 发送长度if err ! nil {// 如果发送失败打印错误信息并返回fmt.Println(Error sending length:, err)return}// 再发送消息_, err conn.Write([]byte(message)) // 发送消息if err ! nil {// 如果发送失败打印错误信息并返回fmt.Println(Error sending message:, err)return}}
}