go 实现tcp 服务器
TCP基础
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。TCP将用户数据打包构成报文段,它发送数据时启动一个定时器,另一端收到数据进行确认,对失序的数据重新排序,丢弃重复的数据。TCP提供一种面向连接的可靠的字节流服务,面向连接意味着两个使用TCP的应用(B/S)在彼此交换数据之前,必须先建立一个TCP连接,类似于打电话过程,先拨号振铃,等待对方说喂,然后应答。在一个TCP连接中,只有两方彼此通信。
GO语言实现
我们将使用 TCP 协议和协程范式编写一个简单的客户端-服务器应用,一个(web)服务器应用需要响应众多客户端的并发请求:**Go 会为每一个客户端产生一个协程用来处理请求。我们需要使用 net 包中网络通信的功能。**它包含了处理 TCP/IP 以及 UDP 协议、域名解析等方法。
golang的net包
golang的net包
参考URL: https://studygolang.com/articles/581
https://golang.org/pkg/net/
import “net”
net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
虽然本包提供了对网络原语的访问,大部分使用者只需要Dial、Listen和Accept函数提供的基本接口;以及相关的Conn和Listener接口。crypto/tls包提供了相同的接口和类似的Dial和Listen函数。
常用,需掌握!
golang中bufio包
golang中bufio包
参考URL: https://www.imooc.com/article/272890
bufio 包介绍
bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。
bufio 是通过缓冲来提高效率
简单的说就是,把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io 从而提高速度。同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。缓冲区的设计是为了存储多次的写入,最后一口气把缓冲区内容写入文件。
常用,需掌握
TCP服务端
go语言中可以每次建立一次链接就创建一个goroutine去处理,使用goroutine实现并发非常方便和高效。
TCP服务端程序的一般处理流程
1.建立并绑定 Socket:首先服务端使用 socket() 函数建立网络套接字,然后使用 bind() 函数为套接字绑定指定的 IP 和端口;
2.监听请求:接下来,服务端使用 listen() 函数监听客户端对绑定 IP 和端口的请求;
3.接收连接:如果有请求过来,并通过三次握手成功建立连接,则使用 accept() 函数接收该链接;
4.创建goroutine去处理链接:服务端通过 read() 函数从上述已建立连接读取客户端发送的请求数据,经过处理后再通过 write() 函数将响应数据发送给客户端
TCP客户端的一般处理流程
1.建立 Socket:客户端同样使用 socket()函数建立网络套接字;
2.建立连接:建立连接:然后调用 connect() 函数传入 IP 和端口号建立与指定服务端网络程序的连接;
3.发送请求与接收响应:连接建立成功后,客户端就可以通过 write() 函数向服务端发送数据,并使用 read() 函数从服务端接收响应。
简单demo示例
server.go
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
fmt.Println("Starting the server ...")
// 创建 listener
listener, err := net.Listen("tcp", "127.0.0.1:50000")
if err != nil {
fmt.Println("Error listening", err.Error())
return
}
// 监听并接受来自客户端的连接
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting", err.Error())
return
}
// 启动一个goroutine处理连接
go process(conn)
}
}
func process(conn net.Conn) {
// 关闭连接
defer conn.Close()
for {
reader := bufio.NewReader(conn)
var buf [128]byte
// 读取数据
len, err := reader.Read(buf[:])
if err != nil {
fmt.Println("read from client failed, err:", err)
break
}
recvStr := string(buf[:len])
fmt.Println("Received data: ", recvStr)
// 发送数据
conn.Write([]byte(recvStr))
}
}
client.go
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
//tcp client demo
func main() {
fmt.Println("Starting the client ...")
//与服务端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:50000")
if err != nil {
fmt.Printf("dial failed, err:%v\n", err)
return
}
//2、利用该连接进行数据的发送和接收
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n')
trimmedInput := strings.Trim(input, "\r\n")
// fmt.Printf("input:--%s--", input)
// fmt.Printf("trimmedInput:--%s--", trimmedInput)
if trimmedInput == "Q" {
return
}
//给服务端发消息
_, err := conn.Write([]byte(trimmedInput))
if err != nil {
fmt.Printf("send failed,err:%v\n", err)
return
}
//从服务端接收回复的消息
var buf [1024]byte
n, err := conn.Read(buf[:])
if err != nil {
fmt.Printf("read failed,err:%v\n", err)
return
}
fmt.Printf("收到服务端回复:%v\n", string(buf[:n]))
}
}
客户端通过 net.Dial 创建了一个和服务器之间的连接。
它通过无限循环从 os.Stdin 接收来自键盘的输入,直到输入了“Q”。注意裁剪 \r 和 \n 字符(仅 Windows 平台需要)。裁剪后的输入被 connection 的 Write 方法发送到服务器。
参考
[推荐]go语言实现TCP服务端以及客户端、解决TCP黏包问题
参考URL: https://blog.csdn.net/qq_40771567/article/details/108433433
在Go中构建并发TCP服务器样例
参考URL: https://zhuanlan.zhihu.com/p/67570906
go实现tcp 服务器
参考URL: https://www.cnblogs.com/lfri/p/11769254.html
go net包简记
参考URL: https://www.cnblogs.com/-wenli/p/12321196.html