Redian新闻
>
在 Go 中实现一个支持并发的 TCP 服务端 | Linux 中国

在 Go 中实现一个支持并发的 TCP 服务端 | Linux 中国

科技
 
导读:仅用大约 65 行代码,开发一个用于生成随机数、支持并发的 TCP 服务端。               
本文字数:5801,阅读时长大约:6分钟

LCTT 译者 :六开箱
🌟🌟🌟🌟
翻译: 61.0 篇
|
贡献: 67 天
2022-03-16
2022-05-21
https://linux.cn/lctt/lkxed

仅用大约 65 行代码,开发一个用于生成随机数、支持并发的 TCP 服务端。

TCP 和 UDP 服务端随处可见,它们基于 TCP/IP 协议栈,通过网络为客户端提供服务。在这篇文章中,我将介绍如何使用 Go 语言🔗 golang.org 开发一个用于返回随机数、支持并发的 TCP 服务端。对于每一个来自 TCP 客户端的连接,它都会启动一个新的 goroutine(轻量级线程)来处理相应的请求。

你可以在 GitHub 上找到本项目的源码:concTcp.go🔗 github.com

处理 TCP 连接

这个程序的主要逻辑在 handleConnection() 函数中,具体实现如下:

  1. func handleConnection(c net.Conn) {
  2.         fmt.Printf("Serving %s\n", c.RemoteAddr().String())
  3.         for {
  4.                 netData, err := bufio.NewReader(c).ReadString('\n')
  5.                 if err != nil {
  6.                         fmt.Println(err)
  7.                         return
  8.                 }
  9.                 temp := strings.TrimSpace(string(netData))
  10.                 if temp == "STOP" {
  11.                         break
  12.                 }
  13.                 result := strconv.Itoa(random()) + "\n"
  14.                 c.Write([]byte(string(result)))
  15.         }
  16.         c.Close()
  17. }

如果 TCP 客户端发送了一个 “STOP” 字符串,为它提供服务的 goroutine 就会终止;否则,TCP 服务端就会返回一个随机数给它。只要客户端不主动终止,服务端就会一直提供服务,这是由 for 循环保证的。具体来说,for 循环中的代码使用了 bufio.NewReader(c).ReadString('\n') 来逐行读取客户端发来的数据,并使用 c.Write([]byte(string(result))) 来返回数据(生成的随机数)。你可以在 Go 的 net 标准包 文档🔗 golang.org 中了解更多。

支持并发

在 main() 函数的实现部分,每当 TCP 服务端收到 TCP 客户端的连接请求,它都会启动一个新的 goroutine 来为这个请求提供服务。

  1. func main() {
  2.         arguments := os.Args
  3.         if len(arguments) == 1 {
  4.                 fmt.Println("Please provide a port number!")
  5.                 return
  6.         }
  7.         PORT := ":" + arguments[1]
  8.         l, err := net.Listen("tcp4", PORT)
  9.         if err != nil {
  10.                 fmt.Println(err)
  11.                 return
  12.         }
  13.         defer l.Close()
  14.         rand.Seed(time.Now().Unix())
  15.         for {
  16.                 c, err := l.Accept()
  17.                 if err != nil {
  18.                         fmt.Println(err)
  19.                         return
  20.                 }
  21.                 go handleConnection(c)
  22.         }
  23. }

首先,main() 确保程序至少有一个命令行参数。注意,现有代码并没有检查这个参数是否为有效的 TCP 端口号。不过,如果它是一个无效的 TCP 端口号,net.Listen() 就会调用失败,并返回一个错误信息,类似下面这样:

  1. $ go run concTCP.go 12a
  2. listen tcp4: lookup tcp4/12a: nodename nor servname provided, or not known
  3. $ go run concTCP.go -10
  4. listen tcp4: address -10: invalid port

net.Listen() 函数用于告诉 Go 接受网络连接,因而承担了服务端的角色。它的返回值类型是 net.Conn,后者实现了 io.Reader 和 io.Writer 接口。此外,main() 函数中还调用了 rand.Seed() 函数,用于初始化随机数生成器。最后,for 循环允许程序一直使用 Accept() 函数来接受 TCP 客户端的连接请求,并以 goroutine 的方式来运行 handleConnection(c) 函数,处理客户端的后续请求。

net.Listen() 的第一个参数

net.Listen() 函数的第一个参数定义了使用的网络类型,而第二个参数定义了服务端监听的地址和端口号。第一个参数的有效值为 tcptcp4tcp6udpudp4udp6ipip4ip6Unix(Unix 套接字)、Unixgram 和 Unixpacket,其中:tcp4udp4 和 ip4 只接受 IPv4 地址,而 tcp6udp6 和 ip6 只接受 IPv6 地址。

服务端并发测试

concTCP.go 需要一个命令行参数,来指定监听的端口号。当它开始服务 TCP 客户端时,你会得到类似下面的输出:

  1. $ go run concTCP.go 8001
  2. Serving 127.0.0.1:62554
  3. Serving 127.0.0.1:62556

netstat 的输出可以确认 congTCP.go 正在为多个 TCP 客户端提供服务,并且仍在继续监听建立连接的请求:

  1. $ netstat -anp TCP | grep 8001
  2. tcp4       0      0  127.0.0.1.8001         127.0.0.1.62556        ESTABLISHED
  3. tcp4       0      0  127.0.0.1.62556        127.0.0.1.8001         ESTABLISHED
  4. tcp4       0      0  127.0.0.1.8001         127.0.0.1.62554        ESTABLISHED
  5. tcp4       0      0  127.0.0.1.62554        127.0.0.1.8001         ESTABLISHED
  6. tcp4       0      0  *.8001                 *.*                    LISTEN

在上面输出中,最后一行显示了有一个进程正在监听 8001 端口,这意味着你可以继续连接 TCP 的 8001 端口。第一行和第二行显示了有一个已建立的 TCP 网络连接,它占用了 8001 和 62556 端口。相似地,第三行和第四行显示了有另一个已建立的 TCP 连接,它占用了 8001 和 62554 端口。

下面这张图片显示了 concTCP.go 在服务多个 TCP 客户端时的输出:

concTCP.go TCP 服务端测试

类似地,下面这张图片显示了两个 TCP 客户端的输出(使用了 nc 工具):

是用 nc 工具作为 concTCP.go 的 TCP 客户端

你可以在 维基百科🔗 en.wikipedia.org 上找到更多关于 nc(即 netcat)的信息。

总结

现在,你学会了如何用大约 65 行 Go 代码来开发一个生成随机数、支持并发的 TCP 服务端,这真是太棒了!如果你想要让你的 TCP 服务端执行别的任务,只需要修改 handleConnection() 函数即可。


via: https://opensource.com/article/18/5/building-concurrent-tcp-server-go

作者:Mihalis Tsoukalos 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出


欢迎遵照 CC-BY-SA 协议规定转载,
如需转载,请在文章下留言 “转载:公众号名称”,
我们将为您添加白名单,授权“转载文章时可以修改”。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
实测 Linux Mint 升级工具 | Linux 中国在美国51. 暗中找工用 Gwenview 在 Linux 上裁剪和调整照片大小 | Linux 中国微软还有另一个 Linux 发行版,而且是基于 Debian 的 | Linux 中国上一个说“丼”不读jǐng的人,已经被我骂哭了使用 watch 和 tail 命令监视 Linux 上的活动 | Linux 中国Linux 内核 5.18 版本正式发布,新增显卡驱动以及硬件支持 | Linux 中国在 Ubuntu Linux 如何安装 H.264 解码器 | Linux 中国好消息!Docker Desktop 现已支持 Linux | Linux 中国最适合程序员的 10 款 Linux 发行版 | Linux 中国开源开发者创建首个支持维护者的基金 | Linux 中国最大的赢家Plex 桌面播放器现已支持 Linux | Linux 中国分享 8 篇使用 Linux 命令行的技巧 | Linux 中国Linux 中国开通播客频道:“开源朗读者”和“硬核观察” | Linux 中国巴黎第二日: 路易威登基金会展览馆使用 dnf 进行 Linux 包管理 | Linux 中国Fedora Linux 37 即将正式支持树莓派 4 | Linux 中国安全常识:开电动车的朋友要注意有了扩展,GNOME Web 正逐渐成为 Linux 桌面上一个有吸引力的选择 | Linux 中国Archinstall 新的菜单系统让安装 Arch Linux 更容易了 | Linux 中国如何在 Linux 和 Windows 电脑之间共享文件 | Linux 中国你的 Linux 启动时有几只小企鹅? | Linux 中国关于老年人再就业诺基亚勒令一个开源 Linux 手机项目 “NOTKIA” 改名字 | Linux 中国在虚拟机中运行 Linux 的十大优点 | Linux 中国如何在 Linux 桌面中启用 “激活 Linux” 水印通知 | Linux 中国HydraPaper:一个支持多显示器的 Linux 壁纸管理器 | Linux 中国在 Linux 上使用 Bash 创建一个临时文件 | Linux 中国使用 apt 进行 Linux 包管理 | Linux 中国Fedora Linux 36 发布 | Linux 中国Fudgie?令人惊叹的 Budgie 桌面即将登陆 Fedora Linux | Linux 中国我如何在 Linux 上扫描家庭照片 | Linux 中国神秘的 GeckoLinux 创建者推出了一个新的 Debian 合成发行版 | Linux 中国英伟达在提升 Linux 上的 GPU 使用体验上迈出了一大步 | Linux 中国
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。