开发一个并发TCP服务器,只使用大约65行Go代码生成随机数。
本文是Mihalis Tsoukalos的Go系列的一部分。阅读第1部分:在Go中创建随机安全密码。
TCP和UDP服务器无处不在,通过TCP / IP网络为网络客户端提供服务。在本文中,我将解释如何使用Go编程语言开发一个返回随机数的并发TCP服务器。对于来自TCP客户端的每个传入连接,TCP服务器将启动一个新的goroutine来处理该请求。
你可以在GitHub上找到这个项目,concTCP.go。
处理TCP连接
该程序的逻辑可以在handleConnection()
函数的Go代码中找到,其实现如下:
func handleConnection(c net.Conn) {
fmt.Printf("Serving %sn", c.RemoteAddr().String())
for {
netData, err := bufio.NewReader(c).ReadString('n')
if err != nil {
fmt.Println(err)
return
}
temp := strings.TrimSpace(string(netData))
if temp == "STOP" {
break
}
result := strconv.Itoa(random()) + "n"
c.Write([]byte(string(result)))
}
c.Close()
}
编程和开发
- 新的Python内容
- 我们最新的JavaScript文章
- 最近的Perl帖子
- 红帽开发者博客
如果TCP客户端发送“STOP”字符串,则服务该特定TCP客户端的goroutine将终止;否则,TCP服务器会将随机数发送回TCP客户端。for
只要TCP客户端需要,循环就确保TCP客户端将被服务。for
循环中的Go代码逐行读取来自TCP客户端的数据,使用bufio.NewReader(c).ReadString('n')
并使用发回数据c.Write([]byte(string(result)))
。(您可能会发现净标准Go包文档很有帮助。)
并发
该main()
函数的实现告诉TCP服务器每次必须为TCP客户端提供服务时都要启动一个新的goroutine:
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide a port number!")
return
}
PORT := ":" + arguments[1]
l, err := net.Listen("tcp4", PORT)
if err != nil {
fmt.Println(err)
return
}
defer l.Close()
rand.Seed(time.Now().Unix())
for {
c, err := l.Accept()
if err != nil {
fmt.Println(err)
return
}
go handleConnection(c)
}
}
首先,main()
确保程序至少有一个命令行参数。请注意,现有代码不会检查给定的命令行参数是否是有效的TCP端口号。但是,如果给定值不是有效的TCP端口号,则调用net.Listen()
将失败,并显示类似于以下内容的错误消息:
$ go run concTCP.go 12a
listen tcp4: lookup tcp4/12a: nodename nor servname provided, or not known
$ go run concTCP.go -10
listen tcp4: address -10: invalid port
该net.Listen()
调用用于告知Go程序接受网络连接,从而充当服务器。返回值net.Listen()
是net.Conn
类型,它实现了io.Reader
和io.Writer
接口。该main()
函数还调用该rand.Seed()
函数以初始化随机数生成器。最后,for
循环允许程序继续使用Accept()
将由handleConnection()
函数实例处理的新TCP客户端,这些实例作为goroutines执行。
net.Listen()的第一个参数
net.Listen()
函数的第一个参数定义将使用的网络类型,而第二个参数定义服务器地址以及服务器将侦听的端口号。第一个参数的有效值为tcp,tcp4(仅限IPv4),tcp6(仅限IPv6),udp,udp4(仅限IPv4),udp6(仅限IPv6),ip,ip4(仅限IPv4),ip6(仅限IPv6),Unix(Unix套接字),Unixgram和Unixpacket。
并发TCP服务器正在运行
concTCP.go需要一个命令行参数,它是它将侦听的端口号。在为TCP客户端提供服务时,您将从concTCP.go获得的输出类似于以下内容:
$ go run concTCP.go 8001
Serving 127.0.0.1:62554
Serving 127.0.0.1:62556
输出netstat(1)
可以验证concTCP.go在侦听更多连接时为多个TCP客户端提供服务:
$ netstat -anp TCP | grep 8001
tcp4 0 0 127.0.0.1.8001 127.0.0.1.62556 ESTABLISHED
tcp4 0 0 127.0.0.1.62556 127.0.0.1.8001 ESTABLISHED
tcp4 0 0 127.0.0.1.8001 127.0.0.1.62554 ESTABLISHED
tcp4 0 0 127.0.0.1.62554 127.0.0.1.8001 ESTABLISHED
tcp4 0 0 *.8001 *.* LISTEN
上一个命令的输出的最后一行通知我们有一个进程侦听端口8001,这意味着您仍然可以连接到TCP端口8001.前两行验证是否存在已建立的TCP网络连接使用类似地,第三和第四行验证存在使用端口号8001和62554的另一个已建立的TCP连接。
此图显示了为多个TCP客户端提供服务时concTCP.go的输出:
concTCP.go TCP服务器正在运行。
类似地,下图显示了使用该nc(1)
实用程序实现的两个TCP客户端的输出:
使用该nc(1)
实用程序作为TCP客户端来concTCP.go。
你可以找到更多的信息nc(1)
,这也叫netcat(1)
,在维基百科上。
摘要
所以,您刚刚学会了如何使用大约65行Go代码开发一个生成随机数的并发TCP服务器,这非常令人印象深刻!如果您希望TCP服务器执行不同的工作,只需更改该handleConnection()
功能的实现。