Node.js 网络通信(二)01-构建TCP服务——TCP全名为传输控制协议,属于传输层协议,如http协议 & 显著特征是在传输之前需要三次握手形成会话 & Socket通信模型
第2章 构建 TCP 服务
TCP 服务在网络应用中十分常见,目前大多数的应用都是基于TCP搭建而成的。
TCP 全名为传输控制协议,在 OSI 模型(由七层模型,分别为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)中属于传输层协议。许多应用层协议基于TCP构建,典型的是HTTP、SMTP、IMAP等协议。
七层协议示意图如下:
层级 | 作用 |
---|---|
应用层 | HTTP、SMTP、IMAP等 |
表示层 | 加密/解密等 |
会话层 | 通信连接/维持会话 |
传输层 | TCP/UDP |
网络层 | IP |
链路层 | 网络特有的链路接口 |
物理层 | 网络物理硬件 |
TCP 是面向连接的协议,其显著的特征是在传输之前需要3次握手形成会话,如下图所示
只有会话形成之后,服务器端和客户端之间才能互相发送数据。在创建会话的过程中,服务器端和客户端分别提供一个套接字,这个两个套接字共同形成一个连接。服务器端与客户端则通过套接字实现两者之间连接通信的操作。下面是一个基于 Socket 套接字编程的网络通信模型。
基本示例
服务端:
const net = require('net'); const server = net.createServer((c) => {
// 'connection' listener console.log('client connected'); c.on('end', () => {
console.log('client disconnected'); }); c.write('hello\r\n'); c.pipe(c); }); server.on('error', (err) => {
throw err; }); server.listen(8124, () => {
console.log('server bound'); });
客户端:
const net = require('net'); const client = net.createConnection({
port: 8124 }, () => {
// 'connect' listener console.log('connected to server!'); client.write('world!\r\n'); }); client.on('data', (data) => {
console.log(data.toString()); client.end(); }); client.on('end', () => {
console.log('disconnected from server'); });
相关 API
官方API文档:https://nodejs.org/dist/latest-v10.x/docs/api/net.html
服务端相关 API
Class: net.Server
- [new net.Server(options][, connectionListener]) 创建服务器
- Event: ‘close’ 当服务器关闭时,触发该事件
- Event: ‘connection’ 当有新的客户端连接进来时,触发该事件
- Event: ‘error’ 当服务器发生错误时,触发该事件
- Event: ‘listening’ 当调用 server.listen() 绑定端口后触发,简洁写法为 server.listen(port, listeningListener),通过 listen() 方法的第二个参数传入
- server.address() 服务器创建侦听成功后,可以用来获取服务器地址相关信息,包含
{ port: 12346, family: 'IPv4', address: '127.0.0.1' }
信息 - server.close([callback]) 当服务器关闭时触发,在调用 server.close() 后,服务器将停止接受新的套接字连接,但保持当前存在的连接,等待所有连接都断开后,会触发该事件
- server.connections 获取当前已建立连接的数量
- 注意:该 API 即将废弃,推荐使用
server.getConnections()
替换
- 注意:该 API 即将废弃,推荐使用
- server.getConnections(callback) 获取当前已建立连接的数量
- server.listen() 绑定端口号启动服务,开始等待侦听
- [server.listen(handle, backlog][, callback])
- [server.listen(options, callback])
- [server.listen(path, backlog][, callback])
- [server.listen(port[, host[, backlog]]][, callback])
- server.listening 获取服务器的侦听状态
- server.maxConnections 在服务器连接数较高的时候,可以通过设置该属性用于拒绝超过最大数的连接
- server.ref() 恢复服务器侦听
- server.unref() 暂停服务器侦听,可以使用
server.ref()
恢复服务器侦听
套接字 Socket 相关 API
Class: net.Socket
- new net.Socket([options]) 创建 Socket 连接
- Event: ‘close’ 当套接字完全关闭时,触发该事件
- Event: ‘connect’ 该事件用于客户端,当套接字与服务端连接成功时会被触发
- Event: ‘data’ 当一端调用
write()
发送数据时,另一端会触发data
事件,事件传递的数据即是write()
发送的数据 - Event: ‘drain’ 当任意一端调用 write() 发送数据时,当前这端会触发该事件
- Event: ‘end’ 当连接中的任意一端发送了 FIN 数据时,将会触发该事件
- Event: ‘error’ 当异常发生时,触发该事件
- Event: ‘timeout’ 当一定时间后连接不再活跃时,该事件将会被触发,通知用户当前连接已经被闲置了
- socket.address() 获取套接字的连接信息,例如
{ port: 12346, family: 'IPv4', address: '127.0.0.1' }
- socket.localAddress 获取当前套接字地址
- socket.localPort 获取当前套接字端口号
- socket.remoteAddress 获取另一端套接字地址
- socket.remoteFamily 获取另一端套接字IP协议版本
- socket.remotePort 获取另一端套接字连接端口号
- socket.setEncoding([encoding]) 设置获取数据解析的编码格式,默认不处理
- socket.write(data[, encoding][, callback]) 通过套接字发送数据
其它 API
- net.connect() 创建 Socket 客户端连接,和
net.createConnection()
作用相等- net.connect(options[, connectListener])
- [net.connect(port, host][, connectListener])
- net.createConnection() 创建 Socket 客户端连接
- net.createConnection(options[, connectListener])
- net.createConnection(path[, connectListener])
- [net.createConnection(port, host][, connectListener])
- [net.createServer(options][, connectionListener]) 创建Socket服务端,等价于
new net.Server()
- net.isIP(input) 判断是否是IP地址
- net.isIPv4(input) 判断是否是符合 IPv4协议的地址
- net.isIPv6(input) 判断是否是符合 IPv6 协议的地址
案例:聊天室
初始化
核心需求
- 用户第一次进来,提示用户输入昵称进行注册
- 昵称不允许重复
- 广播消息(群发)
- 用户昵称
- 点对点消息(私聊)
数据格式设计(语言协议)
- 什么是数据格式
- 数据格式(data format)是描述数据保存在文件或记录中的规则。可以是字符形式的文本格式,或二进制数据形式的压缩格式
- 为什么要进行数据格式设计
- 比较常见的数据传输格式
- JSON
- XML
- YAML
- …
用户登录
客户端:
{
"type": "login", "nickname": "xxx" }
服务端:
{
"type": "login", "success": true | false, "message": "登录成功|失败", "sumUsers": 10 }
广播消息
客户端:
{
"type": "broadcast", "message": "xxx" }
服务端:
{
"type": "broadcast", "nickname": "xxx", "message": "xxx" }
点对点消息
客户端:
{
"type": "p2p", "to": "xxx", "message": "xxx" }
服务端:
{
"type": "p2p", "from": "xxx", "to": "xxx", "message": "xxx" }
上线|离线通知日志
服务端:
{
"type": "log", "message": "xxx 进入|离开了聊天室,当前在线人数:xx" }
用户登录
- 客户端输入昵称发送到服务端
- 服务端接收数据,校验昵称是否重复
- 如果已重复,则发送通知告诉客户端
- 如果可以使用,则将用户昵称及通信 Socket 存储到容器中用于后续使用
群发消息
- 客户端输入消息发送到服务端
- 服务端将消息发送给所有当前连接(也就是存储客户端Socket的容器)的客户端
- 客户端收到消息,将消息输出到终端
私聊
- 客户端输入消息发送到服务端
- 服务端根据消息内容从容器中找到对应的通信客户端,然后将消息发送给该客户端
- 对应的客户端收到消息,将消息输入到终端
上线|离线通知
- 上线通知
- 离线通知
总结
- TCP 必须建立连接(三次握手建立连接)才能通信
- TCP 只是负责数据的传输,不关心传输的数据格式问题
{
"type": "xxx" }
- 如果需要使用 TCP 通信完成某种功能,则需要制定数据格式协议,或者使用第三方协议
- Socket 就是与之通信的另一端,通过 Socket 接收或是发送数据
- Socket 通信模型
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/hd-nodejs/10821.html