并发的Clock服务

2023-09-14 09:13:47

网络编程是并发大显身手的一个领域,由于服务器是最典型的需要同时处理很多连接的程序,这些连接一般来自于彼此独立的客户端。在本小节中,我们会讲解go语言的net包,这个包提供编写一个网络客户端或者服务器程序的基本组件,无论两者间通信是使用TCP、UDP或者Unix domain sockets。在第一章中我们使用过的net/http包里的方法,也算是net包的一部分。

我们的第一个例子是一个顺序执行的时钟服务器,它会每隔一秒钟将当前时间写到客户端:

gopl.io/ch8/clock1

// Clock1 is a TCP server that periodically writes the time. package main import ( "io" "log" "net" "time" ) func main() { listener, err := net.Listen("tcp", "localhost:8000") if err != nil { log.Fatal(err) } for { conn, err := listener.Accept() if err != nil { log.Print(err) // e.g., connection aborted continue } handleConn(conn) // handle one connection at a time } } func handleConn(c net.Conn) { defer c.Close() for { _, err := io.WriteString(c, time.Now().Format("15:04:05\n")) if err != nil { return // e.g., client disconnected } time.Sleep(1 * time.Second) } }

Listen函数创建了一个net.Listener的对象,这个对象会监听一个网络端口上到来的连接,在这个例子里我们用的是TCP的localhost:8000端口。listener对象的Accept方法会直接阻塞,直到一个新的连接被创建,然后会返回一个net.Conn对象来表示这个连接。

handleConn函数会处理一个完整的客户端连接。在一个for死循环中,用time.Now()获取当前时刻,然后写到客户端。由于net.Conn实现了io.Writer接口,我们可以直接向其写入内容。这个死循环会一直执行,直到写入失败。最可能的原因是客户端主动断开连接。这种情况下handleConn函数会用defer调用关闭服务器侧的连接,然后返回到主函数,继续等待下一个连接请求。

time.Time.Format方法提供了一种格式化日期和时间信息的方式。它的参数是一个格式化模板,标识如何来格式化时间,而这个格式化模板限定为Mon Jan 2 03:04:05PM 2006 UTC-0700。有8个部分(周几、月份、一个月的第几天……)。可以以任意的形式来组合前面这个模板;出现在模板中的部分会作为参考来对时间格式进行输出。在上面的例子中我们只用到了小时、分钟和秒。time包里定义了很多标准时间格式,比如time.RFC1123。在进行格式化的逆向操作time.Parse时,也会用到同样的策略。(译注:这是go语言和其它语言相比比较奇葩的一个地方。你需要记住格式化字符串是1月2日下午3点4分5秒零六年UTC-0700,而不像其它语言那样Y-m-d H:i:s一样,当然了这里可以用1234567的方式来记忆,倒是也不麻烦。)

为了连接例子里的服务器,我们需要一个客户端程序,比如netcat这个工具(nc命令),这个工具可以用来执行网络连接操作。

$ go build gopl.io/ch8/clock1 $ ./clock1 & $ nc localhost 8000 13:58:54 13:58:55 13:58:56 13:58:57 ^C

客户端将服务器发来的时间显示了出来,我们用Control+C来中断客户端的执行,在Unix系统上,你会看到^C这样的响应。如果你的系统没有装nc这个工具,你可以用telnet来实现同样的效果,或者也可以用我们下面的这个用go写的简单的telnet程序,用net.Dial就可以简单地创建一个TCP连接:

gopl.io/ch8/netcat1

// Netcat1 is a read-only TCP client. package main import ( "io" "log" "net" "os" ) func main() { conn, err := net.Dial("tcp", "localhost:8000") if err != nil { log.Fatal(err) } defer conn.Close() mustCopy(os.Stdout, conn) } func mustCopy(dst io.Writer, src io.Reader) { if _, err := io.Copy(dst, src); err != nil { log.Fatal(err) } }

这个程序会从连接中读取数据,并将读到的内容写到标准输出中,直到遇到end of file的条件或者发生错误。mustCopy这个函数我们在本节的几个例子中都会用到。让我们同时运行两个客户端来进行一个测试,这里可以开两个终端窗口,下面左边的是其中的一个的输出,右边的是另一个的输出:

$ go build gopl.io/ch8/netcat1 $ ./netcat1 13:58:54 $ ./netcat1 13:58:55 13:58:56 ^C 13:58:57 13:58:58 13:58:59 ^C $ killall clock1

killall命令是一个Unix命令行工具,可以用给定的进程名来杀掉所有名字匹配的进程。

第二个客户端必须等待第一个客户端完成工作,这样服务端才能继续向后执行;因为我们这里的服务器程序同一时间只能处理一个客户端连接。我们这里对服务端程序做一点小改动,使其支持并发:在handleConn函数调用的地方增加go关键字,让每一次handleConn的调用都进入一个独立的goroutine。

gopl.io/ch8/clock2

for { conn, err := listener.Accept() if err != nil { log.Print(err) // e.g., connection aborted continue } go handleConn(conn) // handle connections concurrently }

现在多个客户端可以同时接收到时间了:

$ go build gopl.io/ch8/clock2 $ ./clock2 & $ go build gopl.io/ch8/netcat1 $ ./netcat1 14:02:54 $ ./netcat1 14:02:55 14:02:55 14:02:56 14:02:56 14:02:57 ^C 14:02:58 14:02:59 $ ./netcat1 14:03:00 14:03:00 14:03:01 14:03:01 ^C 14:03:02 ^C $ killall clock2

更多推荐

从零开始在树莓派上搭建WordPress博客网站并实现公网访问

文章目录序幕概述1.安装PHP2.安装MySQL数据库3.安装Wordpress4.设置您的WordPress数据库设置MySQL/MariaDB创建WordPress数据库5.WordPressconfiguration6.将WordPress站点发布到公网安装相对URL插件修改config.php配置7.支持好友链

php程序设计的基本原则

单一职责原则(SRP):一个类应该只有一个原因引起变化,即一个类应该只负责一项职责。classUser{private$name;private$email;publicfunction__construct($name,$email){$this->name=$name;$this->email=$email;}pu

HTML5 游戏开发实战 | 俄罗斯方块

俄罗斯方块是一款风靡全球的电视游戏机和掌上游戏机游戏,它曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事。这款游戏看似简单但却变化无穷,游戏过程仅需要玩家将不断下落的各种形状的方块移动、翻转,如果某一行被方块充满了,那就将这一行消掉;而当窗口中无法再容纳下落的方块时,就宣告游戏结束。可见俄罗斯方块的需求如下。(

AI+游戏线下沙龙活动暨COC上海城市开发者社区8月活动

引言近年来,随着人工智能技术的不断发展和游戏开发技术的不断更新,越来越多的游戏公司开始将人工智能技术应用于游戏领域,以提高开发效率、降低成本,实现游戏玩家更好的游戏体验。为了探讨AI+游戏的技术实践经验,近日在亚马逊会议中心举办了一场以AI+游戏为主题的技术研讨会。在AI+游戏的技术实践研讨会上,与会者们分享了一些关于

云游戏下,会带来哪些技术变革

云游戏前言元宇宙是什么?云游戏的福利云游戏包括哪些技术前言大家好,在这里给大家介绍一个新名词----云游戏。可能有一些小伙伴了解过一些,也有一些小伙伴可能没有了解过,那这里就带大家了解一下元宇宙里的云游戏。2023年,如果说到什么最火🔥,什么最流行,那肯定是非元宇宙莫属了。自从2021年来,元宇宙就如同那雨后春笋一样

方案:数智化视频AI技术为智慧防汛筑基,构建防汛“数字堤坝”

一、背景分析在过去的几年中,全球气候变化导致许多城市在雨季面临严重的洪涝灾害。这些灾害不仅对人们的生命安全和财产造成威胁,也影响了城市的正常运转。传统的防汛手段主要依赖人力监控和应急指挥,但存在响应速度慢、处理效率低等问题。因此,我们需要一种智能、高效的解决方案来提高防汛效率和降低洪涝灾害的影响。随着新一代信息技术的普

【JavaSE专栏49】Java集合类LinkedList解析,链表和顺序表有什么不同?

作者主页:Designer小郑作者简介:3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN学院、蓝桥云课认证讲师。主打方向:Vue、SpringBoot、微信小程序本文讲解了Java中集合类LinkedList的语法、使用说明和应用场景,并给出了样例代码。目录一、什么是Lin

[C++入门]---List的使用及模拟实现

文章目录1.list的介绍2.list的使用2.1list的构造函数2.2listmodifiers2.3listcapacity2.4listelmentaccess2.5iterator的使用3.list的模拟实现3.1list的源码1.list的介绍list是可以在常数范围内在任意位置进行插入和删除的序列式容器,

【Java 基础篇】深入理解Java HashMap:使用注意事项和性能优化

Java是一种广泛使用的编程语言,而集合是Java编程中不可或缺的一部分。在Java的集合框架中,HashMap是一个常用的数据结构,用于存储键值对。本文将深入介绍HashMap集合,从基础到高级用法,帮助您更好地理解和利用它。什么是HashMap?HashMap是Java集合框架中的一个类,它实现了Map接口,用于存

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows大家好我是艾西,最近研究了下l4d2(求生之路2)这款游戏的搭建以及架设过程。今天就给喜欢l4d2这款游戏的小伙伴们分享下怎么搭建架设一个自己的服务器。毕竟自己当服主是热爱游戏每一个人的梦想,在自己的服务器里为所欲为在游戏里成就自己。(注:因PT原因本文

RocketMQ消息的分类

一、普通消息1消息发送分类Producer对于消息的发送方式也有多种选择,不同的方式会产生不同的系统效果。同步发送消息同步发送消息是指,Producer发出⼀条消息后,会在收到MQ返回的ACK之后才发下⼀条消息。该方式的消息可靠性最高,但消息发送效率太低。异步发送消息异步发送消息是指,Producer发出消息后无需等待

热文推荐