为 Linux 编写字母数字寻呼服务器

作者:Erik Max Francis

字母数字寻呼——至少我们在这里考虑的类型——是通过调制解调器传送的。要发送寻呼,我们连接到我们的服务提供商,通过其指定的协议与其通信,接收寻呼已传送的确认,然后断开连接。

要向某人发送消息,我们需要了解关于他们的两件事:他们的寻呼服务拨号号码和他们的 PIN(个人识别号码)。字母数字寻呼机的所有者应该能够联系他们的寻呼服务提供商并轻松获得此信息。

协议

几种协议用于传送寻呼。最流行的(也是最简单的)是 IXO,以发明它的公司命名。它也被称为 TAP(Telocator 字母输入协议)和 PET(个人输入终端);这些名称是在其历史进程中由其他公司分配给它的。

在本文中,我将只讨论 IXO 协议。它有点复杂,所以我不会详细介绍,但它相对简单明了。简而言之,以下是远程拨号站点(我们,在传送寻呼时)和寻呼服务提供商之间整体事务的描述。

首先,我们建立连接并引起服务的注意。在服务确认我们发送消息的意图后,我们声明我们希望进入自动模式——这是传输消息的最简单方法,尽管在极少数情况下我们可能希望使用手动模式。

我们等待服务确认我们的请求。当我们收到发送消息内容的许可信号时,我们发送一系列字段。通常我们会发送两个字段,包括我们希望向其发送消息的寻呼机的 PIN 和消息。我们在消息后跟一个校验和,以便提供商可以(在某种程度上)验证我们的消息是否已被忠实接收。最后,一旦我们收到消息已接收的确认,我们礼貌地请求断开连接并断开载波。

IXO 协议(参见资源)支持在单个事务中向同一服务发送多个寻呼,但不一定发送给同一寻呼机。因此,如果我们发现许多寻呼同时发送给使用同一服务(即同一拨号号码)的人员,则此方法为大规模服务提供了优化的可能性。

配置文件和设备

首先,我们需要一种机制来识别我们想要向其发送消息的每个寻呼机;我们将其称为 配置文件。如前所述,为了能够向特定寻呼机发送消息,我们需要两件事:其寻呼服务电话号码和该特定寻呼机的 PIN。这些可以保存在配置文件中,管理程序可以编辑和删除这些文件。

如果我们想支持多个调制解调器——即同时传送多个寻呼的能力——我们还需要了解并跟踪物理调制解调器。我们将其称为 设备。与配置文件一样,我们将把分配给我们服务器的设备列表保存在配置文件中。

并发寻呼传送应利用多进程处理;使用 fork 与不同的设备同时发送多个寻呼。这使得整体流程控制变得更加简单,并且不会造成任何重大问题,因为我们可以简单地通过 waitpid 检查子进程的退出状态(成功或失败)并采取相应的操作。

我们将手头保留设备列表,并且当我们使用其中一个设备传送寻呼时,将其标记为忙碌。除非我们要在调制解调器专用于我们服务器的系统上运行,否则我们几乎肯定希望使用标准锁文件,除了将设备标记为已使用之外。(即使调制解调器确实是专用的,小心谨慎一点也没有坏处。)这将防止其他进程在我们服务器实际传送寻呼的过程中干扰我们的服务器。

过滤器

字母数字寻呼机在消息可以显示的字符数方面往往受到限制;使用 IXO 协议的典型最大长度支持为 256 个字符,包括表示为字符串的 PIN,因此在实践中通常更接近 250 个字符。

经常收到大量寻呼的人经常遇到此限制,尤其是在发送较长消息(如电子邮件或注释)时。一个有用的解决方案是实现一个 过滤器,它处理要发送的消息并执行简单的搜索和替换,以便用较短的等效项替换常用的单词和短语。例如,单词“and”可以用与号 (&) 表示,“talk to you later”可以缩写为 TTYL,等等。

过滤器也可以用于通过模糊处理来提供某种形式的安全性。对于大多数寻呼机,通过无线电波传送的消息是不安全的;任何拥有正确设备的人都可以拦截它们。这显然意味着,普通人可能不希望他们的姓名、地址和其他私人信息通过无线电波传输。一个简单的过滤器选择可以将这些敏感信息替换为寻呼接收方可识别的缩写,但对其他人来说毫无意义。

基于正则表达式实现过滤器也很简单,可以使用 POSIX 正则库。由于简单搜索和正则表达式搜索都相对简单,我将把它们作为练习留给读者。

服务器

服务器是我们操作的主力。服务器处理寻呼传送请求(我们将其称为 作业),然后通过可用的设备通过 IXO 协议传送它们。

作业由两个非常简单的东西组成:要使用的配置文件和要发送的消息。服务器如何接收每个作业取决于我们希望如何构建它。我将在此处讨论两种基本方法:基于 TELNET 的服务器和基于文件的服务器。每种方法都有优点和缺点,您选择实现哪种方法将很大程度上取决于您想要支持哪种类型的客户端。

基于 TELNET 的服务器监听主机上的特定端口,并使用简单的协议发送配置文件和消息。如下协议

profile

应该足够了(当然,前提是标识协议的字符串不包含换行符)。

出于安全原因,此类服务器应在接受传送作业之前要求某种形式的身份验证。我们应该使用的身份验证类型将很大程度上取决于我们的特定安全需求,并且超出了本文的范围。

另一方面,基于文件的服务器监视主机上的特定目录(暂停一小段时间,然后扫描其内容)并读取每个新文件,为其创建一个作业,然后处理它;当服务器不再需要该文件时,可以删除该文件。此文件的格式无需与基于 TELNET 的服务器的协议不同

profile

与基于 TELNET 的服务器一样,基于文件的服务器也需要考虑安全问题。服务器将以特定组身份运行,并且只有属于该组的程序才可以提交作业。创建作业文件时,它们必须归该组所有,并被赋予组可写权限(在组可写目录中),以便服务器可以在完成文件后删除文件。身份验证方案可能也在这里有用;至少,一些简单的加密很可能是有序的。

基于 TELNET 的服务器和基于文件的服务器各有优势。基于 TELNET 的服务器最明显的优势是它快速而简单,并且客户端可以在可以使用 telnet 命令从服务器访问的任何机器上运行。(在没有充分的安全考虑的情况下,这也可能是一个 liability。)

基于文件的服务器的客户端必须在同一台机器上运行(为了简单起见,忽略 NFS 挂载的目录),并且必须由属于相应组的用户运行。基于文件的服务器最明显的优势是,在服务器关闭或机器重启的情况下,待处理的作业仍将等待,而基于 TELNET 的作业如果关闭,则必须将其待处理的作业卸载到磁盘(或另一台服务器)。

客户端

客户端是我们用来向服务器发送作业的程序。此时,编写客户端应该很容易,因为通过选择我们要实现的服务器类型,我们已经隐式地选择了我们必须用来与其通信的客户端类型。

客户端需要知道如何联系服务器,以及验证配置文件是否有效(客户端应该知道何时被要求向不存在的寻呼机发送消息)。首先,我们需要一个基本客户端,我们可以使用命令行(或可能是 STDIN)运行该客户端来指定配置文件和消息并传送作业——没有花哨的功能,没有花哨的界面。

对于基于文件的服务器,这是一个程序(仅可由相应组的成员执行),它在具有适当权限的相应目录中创建作业文件。对于基于 TELNET 的服务器,该程序可以在任何可以通过 TELNET 访问运行服务器的机器的机器上运行,并通过 TELNET 传送消息。

一旦我们有了基本客户端,我们就可以继续开发其他类型的客户端。以下是我们可能想要支持的两种

  • 基于电子邮件的客户端:类似 procmail 的东西可以帮助我们完成这里困难的工作——procmail 可以有选择地处理传入的电子邮件并将其路由到我们选择的程序。然后,该程序可以挑选出我们想要传送的相关标头(例如,发件人、收件人和主题)以及电子邮件的正文,然后将其传递给基本客户端。

  • 基于 Web 的客户端:CGI 脚本可以在这里完成工作,挑选出适当的表单实体并将其传递给客户端。在基于文件的服务器中,我们必须确保 Web 服务器运行的用户(通常是“nobody”)也是寻呼机组的成员。(确保在添加后向服务器发送 HUP 信号。)

在这两种情况下,都有安全问题需要处理。我们当然不希望任何发现电子邮件或 Web 网关的人都能向他们想要的任何人发送任何寻呼。关于建议采取何种安全预防措施的具体细节将很大程度上取决于我们正在运行的服务类型,因此我将不在此处详细介绍它们。

将所有部件组合在一起

现在我们可以将拼图的各个部分组合在一起,以构建我们的服务器。

主服务器将读取配置文件和数据文件(在哪里找到东西、它知道的协议列表、可用设备等)。然后它进入其主循环。

如果我们支持多个设备,请使用 waitpid 及其 WNOHANG 选项(以便服务器仅查看是否有任何子进程已完成,而不是无限期地等待——并阻止所有其他服务器活动,直到子进程 确实 完成)以确定是否有任何作业已完成传送;如果已完成,则从队列中删除它们。

然后,检查以查看设备是否可用且新作业是否准备好传送;如果两者都为真,则采取以下步骤。首先,从作业中确定配置文件和消息。然后,获取下一个可用设备并锁定它,以便我们的传送不会受到任何干扰。之后,过滤消息(如果适用),最后,启动一个子进程来传送它。

摘要

资源

Erik Max Francis 是一位 UNIX 工程师,居住在加利福尼亚州圣何塞。他的主要兴趣是编程、Linux、物理学和数学。自内核版本 1.2.8 以来,他一直在家中专门使用 Linux,并且自 1989 年以来一直热衷于阅读和贡献 Usenet。可以通过电子邮件 max@alcyone.com 与他联系。

加载 Disqus 评论