固定链接 使用 PM2 和 Nginx 开发 Node.js TCP 应用程序

使用 PM2 和 Nginx 开发 Node.js TCP 应用程序

使用 PM2 和 Nginx 开发 Node.js TCP 应用程序

TCP 服务器可以接受 TCP 连接请求,建立连接后,双方可以交换数据流。使用 Node.js 可以非常快捷的创建一个 TCP 应用程序,包括服务器和客户端。

TCP 服务器监听特定端口,并且可以同时对多个客户端的请求做出响应,即使客户端断开连接,服务器也始终保持运行状态,这样就可以保证客户端能够多次对服务器进行访问。而 TCP 客户端,用于访问 TCP 服务器,并与之进行通信。客户端向服务器发送数据并接收后就断开连接,即只进行一次访问。如果要让多个客户端同时访问服务器,只需同时运行多个客户端实例。

本教程将使用 Node.js 搭建一个基本 TCP 服务器,以及一个用于测试服务器的客户端。搭建过程中会使用到 PM2,一个强大的 node 进程管理工具,可以利用它来简化很多 node 应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。同时还会使用到 Nginx,用于反向代理。

准备工作

要完成本教程,需要做以下准备工作:

  • 一台服务器,搭载 Ubuntu 16.04 操作系统,本教程中使用的是滴滴云服务器,可以选择默认安装 Ubuntu 16.04,非常方便。
  • 服务器安装 Nginx
  • 服务器安装 Node.js

创建 TCP 服务器

Node.js 中创建 TCP 服务器和客户端均需要用到 net 模块,它提供了一些用于底层的网络通信的小工具。

首先,在服务器上创建一个目录 tcp-nodejs-app,我们会在这个目录中创建整个应用程序:

切换到这个目录:

创建一个名为 package.json 的文件,此文件将列出整个应用程序的所有依赖以及定义运行脚本。其实也可以直接使用 npm init 命令来自动生成 package.json,但还是避免不了手动修改文件内容以适应我们的项目,所以这里就简单的直接手动创建:

编辑生成的文件,做出如下修改。下面代码的作用是指定应用程序的名称、版本、主文件、启动应用程序的命令以及声明软件许可证:

scripts 是应用程序的命令,可以在此处指定的运行命令的脚本,如上设置后可以通过 npm start 来代替 node server.js 来启动应用程序。

接下来创建服务器部分,在应用程序目录中,创建一个 server.js文件:

首先引入 net 模块,并定义服务器的端口和主机:

应用程序的端口使用 7070,host 设置为 127.0.0.1 以确保服务器只监听本地网络接口,稍后我们会使用 Nginx 做反向代理。

调用 net 模块中的 createServer 方法创建一个 TCP 服务,调用 listen 方法监听端口和主机:

启动服务器:

控制台上会看到这个输出:

TCP 服务正在端口 7070 上运行,但也仅仅只是运行着,还没有任何功能可言。下面我们给服务器加个简单的功能:监听多个客户端,并将接受到的数据回传给所有连接中的客户端。完成这个功能需要以两步:

  1. 当客户端连接到服务器时,服务器会触发 connection 事件,监听这个事件,构造一个存放所有连接的客户端的列表(sockets)。

  2. 当服务器接收到客户端的数据时,会触发 data 事件,监听这个事件,处理来自连接客户端的数据流,并将数据广播给列表(sockets)中的所有客户端。

再扩充一下功能,客户端终止连接时将触发服务器的 close 事件,监听这个事件,每当一个客户端断开连接时,从客户端列表中删除该项,不再向其广播:

以下是完整的代码 server.js:

再次启动服务器:

到期目前为止,我们创建了一个功能齐全的 TCP 服务器,接下来编写一个客户端程序来连接到我们的服务器。

创建 TCP 客户端

服务器程序已经有了,下面创建一个客户端程序来与服务器建立连接,进行交互,测试刚才实现的功能。为保持服务器运行,打开一个新的终端窗口登录服务器,来完成下面所有操作:

登录后,进入 tcp-nodejs-app 工作目录:

创建客户端程序文件 client.js:

添加以下代码,创建和服务器之间的连接:

上述代码首先通过 new net.Socket() 创建了一个客户端,调用客户端的 connect 方法建立和服务器的连接。然后调用客户端 client.write 方法,将数据发送到服务器。

当客户端从服务器接收到数据,可以通过监听客户端的 data 事件来打印出服务器的响应:

最后,当与服务器断开连接时报告出来:

这样我们的客户端功能也完成了,运行以下命令以启动客户端:

控制台中可以看到以下输出,说明连接建立,服务器接收到数据并将其回传给客户端:

切换到运行服务器的终端,可以看到以下输出:

服务器和客户端应用程序之间建立 TCP 连接的功能验证通过了。下一步,我们将使用 PM2 启动服务器并将其在后台运行。

使用 PM2 运行服务器

现在我们的 TCP 服务器可以接收客户端连接,但它始终在前台运行,不方便使用。可以利用使用 PM2 运行服务器,使其在后台运行。

首先安装 PM2:

安装 PM2 后,使用它来运行服务器:

服务器现在正在后台运行,但是,如果我们重启机器,这个服务就不再运行了,所以要为它创建一个 systemd 服务,用来守护进程。

运行以下命令以生成和安装 PM2 的 systemd 启动脚本:

执行成功后,PM2 就作为 systemd 服务运行,可以使用 pm2 list 命令列出 PM2 正在管理的所有进程:

可以在列表中看到我们的 TCP 服务器应用程序,ID 为 0。

如果要更改服务器代码,则需要重新启动应用程序的进程以应用更改,如下所示:

使用 PM2 管理该服务器应用程序后方便了很多,下面我们将使用 Nginx 代理对服务器的请求。

设置 Nginx 反向代理

现在 TCP 服务器应用正在运行并侦听 127.0.0.1:7000,这意味着它只能接受来自本台服务器上的TCP 连接。我们可以利用 Nginx 反向代理,将其他机器的请求指向我们的 TCP 服务器。

修改 Nginx 的配置,将 TCP 连接转发到我们的 TCP 服务器。注意必须编辑 Nginx 主配置文件,因为配置 TCP 连接转发的 stream 是顶级块:

在配置文件的末尾添加以下配置:

Nginx 加载此配置后会侦听本台机器的 TCP 连接,将所有访问 3000 端口的请求都代理到 127.0.0.1:7070,也就是我们的 TCP 服务器。proxy_protocol 作用是告诉 Nginx 使用 PROXY 协议将客户端信息发送到后端服务器,后端服务器可以根据需要处理该信息。

保存配置,重新启动 Nginx 以启用代理功能:

接下来,使用 ufw 以允许 3000 端口:

到这里这就完成了 Nginx 反向代理的设置。

测试客户端和服务器连接

因为有了反向代理,我们可以使用任意一台计算机来启动客户端连接 TCP 服务器,可以把 client.js 下载到自己的电脑上运行,来测试是否能成功连接到我们在服务器上创建的 TCP 服务器。

在本地计算机上可以使用以下命令来下载 client.js 文件:

下载完成后,修改 client.js,更改 port 为 3000 并更改 host 为服务器的 IP 地址:

在本地电脑上启动客户端进行测试:

此时可以看到和之前运行时相同的输出,说明本地计算机的 TCP 客户端已通过 Nginx 代理连接了服务器上的 TCP 服务器应用程序:

本来 Nginx 代理了客户端与服务器的连接,我们搭建的 TCP 服务器将无法看到客户端的真实 IP 地址,只能看到 Nginx 的 IP 地址。但是我们在 Nginx 中启用了 PROXY 协议,因此就可以接收到真实的 IP。如果服务器应用程序需要该 IP 地址,则可以调整服务器以处理 PROXY 请求并解析所需的数据。

总结

在本教程中我们使用 Node.js 创建了一个 TCP 应用程序,使用 PM2 让其在后台运行,并且利用 Nginx 将客户端的 TCP 请求代理到这个服务器应用上。同时还创建了一个客户端应用程序,可以在任意一台计算机上运行,来连接前面创建的服务器。有了这个搭建好的基础,后续可以逐步丰富功能,比如搭建一个实时通讯应用程序。


参考文章

本文作者:龚道林

您的留言将激励我们越做越好