Node.js 网络编程(下)实现TCPUDPWebSocket的创建

Posted YuLong~W

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js 网络编程(下)实现TCPUDPWebSocket的创建相关的知识,希望对你有一定的参考价值。

TCP服务器与客户端

TCP基础

TCP协议传输控制协议,提供面向连接的、可靠的数据传输服务

  • 面向连接:数据传输之前,客户端与服务器要建立连接
  • 可靠的:数据的传输是有序的、要对数据进行校验

TCP数据分组称为(Segment)。TCP将来自应用层的数据进行分块并封装成TCP段进行发送

TCP连接的建立采用三次握手方法

Scoket:网络套接字,是进行网络连接的数据结构(服务器的Socket和客户端的Socket)

Socket之间的连接过程包括以下3个步骤

  1. 服务器监听。服务器端Socket处于等待连接的状态,实时监控网络状态
  2. 客户端请求。客户端Socket提出连接请求,要连接的目标是服务器端的Socket
  3. 连接确认

net模块

net模块:实现TCP的连接和服务,封装了大多数的底层的类和服务,方便用户实现TCP协议

导入该模块:

const net = require('net');

1、net.Server类:

可直接用于创建net.Server对象

  • const server = new net.Server([options][, connectionlistener])

实现的事件:

事件描述
close当服务器关闭时被触发。如果有连接存在,则直到所有连接结束才会触发这个事件
connection当一个新的连接建立时被触发,Socket是一个net.Socket的实例对象
error发生错误时被触发
listening当服务被绑定后调用server.listen()方法

提供的方法:

  • server.address() 方法用于返回绑定的ip地址、地址族和服务端口
  • server.close() 方法使服务器停止接受建立新的连接并保持现有的连接
  • server.listen() 方法用来启动一个服务器来监听连接

net.createServer()方法:

  • 语法格式:net.createServer([options][, connectionlistener])
  • 返回一个net.Server类的实例(对象)
  • 该方法可以创建一个TCP服务器,是创建net.Server对象的快捷方式

2、net.Socket类:

可以直接创建net.Socket对象

  • const client = net.Socket([options])

实现的事件:

事件描述
close当Socket完全关闭时发出该事件
connect当一个Socket连接成功建立时触发该事件
data当接收到数据时触发该事件
drain当写入缓冲区变为空时触发该事件
end当Socket的另一端发送一个FIN包时触发该事件,从而结束Socket的可读端
error当错误发生时触发该事件,close事件也会紧接着该事件被触发
ready当Socket准备使用时触发该事件,connect事件发生后立即触发该事件
timeout当Socket超时时触发该事件

提供的方法:

  • socket.connect() 方法用于在指定的Socket上启动一个连接
  • socket.write() 方法用于在Socket上发送数据

net.createConnection()方法:

  • 语法格式:

    • net.createConnection(options[, connectListener])
    • net.createConnection(port[, host][, connectListener])
  • 返回启动连接的net.Socket对象

  • 当连接建立之后,在返回的Socket上将触发一个connect事件

创建TCP服务器和客户端

创建TCP服务器:

//导入net模块
const net=require('net');
//创建TCP服务器
const server=net.createServer((client)=>{ //client表示客户端
    var clientNo=0;//为每个连接到服务端的客户端编号
    console.log(clientNo+'号客户端连接');

    //绑定 end 事件 客户端断开连接时触发
    client.on('end',function () {
        console.log(clientNo+'号客户端端口连接');
    });
    client.write(clientNo+'号客户端,你好');
    client.pipe(client); //通过管道操作

    //绑定 data 事件 接收客户端的数据时触发
    client.on('data',(data=>{
        console.log(clientNo+'号客户端发送的数据是:'+data.toString());
    }));
});
//绑定 err 事件 连接发生错误时触发
server.on('error',(err=> {
    throw err;
}));
//启动监听:指定监听的端口号
server.listen(8234,()=>{
    console.log('TCP服务器已经启动');
});

创建TCP客户端:

const net=require('net');
//创建TCP客户端
var client=net.Socket();
//连接服务器 调用 connect方法
client.connect(8234,'127.0.0.1',()=>{
    console.log('已经连接到服务器');
    client.write('我是TCP客户端');//向服务器发送数据
});
//接收服务器端数据 绑定 data 事件
client.on('data',(data)=>{
    console.log('接收的服务器端数据:'+data.toString());
});
//绑定 end 事件
client.on('end',()=>{
    console.log('结束数据');
});

UDP服务器与客户端

UDP基础

UDP协议用户数据报协议,提供的是不可靠的、面向无连接的传输服务(只有数据的发送方和接收方)

  • 面向无连接:在传输数据之前没有明确的连接链路(即不是所有的数据都是通过一条链路传输)
  • 不可靠的:数据传输不是通过一条链路完成的,因此接收方法接受的数据不一定是按发送的顺序接受,可能造成数据包的丢失

UDP传输给IP的数据单元称作UDP数据报(Datagram)

UDP使用 端口号 为不同的应用保留其各自的数据传输通道。UDP使用Socket,只不过是无连接的数据报Socket

dgram模块

dgram模块: 用于实现UDP通信

导入该模块:

const dgram = require('dgram');

1、dgram.Socket类:

  • dgram.Socket类提供实现UDP应用的基本框架

  • dgram.Socket对象是一个封装了数据报功能的事件触发器

  • dgram.Socket实例由dgram.createSocket() 方法创建

实现的事件:

事件描述
close使用close()方法关闭一个Socket之后触发该事件
error发生任何错误都会触发该事件
listening当一个Socket开始监听数据报信息时触发该事件
message当有新的数据报被Socket接收时触发该事件。

提供的方法:

  • socket.bind() 方法用于设置Socket在指定的端口和地址上监听数据报信息
  • socket.send() 方法用于在Socket上发送一个数据报

dgram.createSocket()方法:

  • 语法格式:
    • dgram.createSocket(options[, callback])
    • dgram.createSocket(type[, callback]) (创建一个特定类型的dgram.Socket对象)
  • 该方法用于创建dgram.Socket对象
  • 一旦创建了Socket,调用 socket.bind() 方法会指示Socket开始监听数据报消息

创建UDP服务器和客户端

创建UDP服务器:

//导入dgram模块
const dgram=require('dgram');
//创建dgram.Socket对象(服务器端-----数据的接收端)
const server =dgram.createSocket('udp4');
//绑定 error 事件
server.on('error',(err => {
    console.log(`服务器异常:${err.stack}`);
}))
//绑定 message 事件 参数msg 客户端发送的数据 参数rinfo 封装了客户端的信息(包括地址、端口号等)
server.on('message',((msg,rinfo)=>{
    var str_msg=msg.toString();
    console.log(`服务器接收来自:${rinfo.address}:${rinfo.port}${str_msg}`);
    console.log(rinfo);
    //向客户端发送事件
    server.send('我问你爱不归期',rinfo.port,rinfo.address);
}))
//注册监听
server.on('listening',()=>{
    //获取服务器地址
    var server_address=server.address();
    console.log(`服务器监听:${server_address.address}:${server_address.port}`);
})
//绑定端口
server.bind(41234);

创建UDP客户端:

const dgram=require('dgram');
const client_msg=Buffer.from('你好,我是UDP客户端');
//创建客户端的Socket
const client=dgram.createSocket('udp4');
//绑定事件
client.on('close',()=>{
    console.log('Socket已关闭');
})
client.on('error',(err =>{
    console.log(err);
}))
client.on('message',((msg,rinfo)=>{
    if (msg=='exit'){
        client.close();
    }
    console.log(`接收到来自服务器:${rinfo.address}:${rinfo.port}的信息:${msg.toString()}`);
}))
//向服务器发送数据
client.send(client_msg,41234,'localhost',(error => {
    if (error){
        client.close();
    }
}))

WebSocket服务器与客户端

HTTP存在的问题:

  • 每次客户端和服务器端的交互都是一次HTTP的请求和应答的过程,增加了每次传输的数据量,浪费带宽资源。
  • 不是真正的实时技术,只是模拟实时的效果,轮询会造成同步延迟。
  • 编程比较复杂,尤其是要模拟比较真实的实时效果时。

WebSocket协议突破HTTP的局限性,节省服务器资源和带宽,解决两端的实时数据传输问题

WebSocket实现机制

  1. 由客户端发起握手,建立连接阶段必须依赖HTTP进行一次握手。两端之间的WebSocket连接建立的同时服务器完成了协议升级,由HTTP升级为WebSocket。连接会一直保持,直到客户端或者服务器任何一方主动关闭。
  2. 进入数据交换阶段,客户端与服务端可以互相主动发送消息。此阶段数据直接通过TCP通道传输,不再依赖HTTP。WebSocket的数据传输是以 帧(Frame) 的形式传输的。
  3. 关闭连接,可以由任意一端发起关闭连接的命令。

WebSocket通信过程:

WebSocket协议的应用场合

  • 实时通信:聊天应用
  • 实时展示和分析:典型的有实时计数器、图表、日志客户端等
  • 文档协同:允许多个用户同时编辑一个文档,且用户能够看到每个用户做出的修改

1、WebSocket服务器:

WebSocket库:ws、WebSocket-Node、faye-websocket-node和socket.io

创建WebSocket服务器对象语法格式:new WebSocket.Server(options[, callback])

WebSocket服务器内置事件

事件描述
close服务器关闭时被触发。
connection成功握手连接时触发。
error发生错误时被触发,可注入一个Error对象。
headers握手前被触发,允许在发送HTTP头之前检查和修改标题。
listening绑定端口时被触发。

2、WebSocket客户端:

创建WebSocket客户端对象语法格式:
new WebSocket(address[, protocols][, options])

WebSocket客户端内置事件:

事件描述
close连接关闭时被触发,有两个参数code(状态码)和reason(原因)。
error发生错误时被触发,有一个参数error(错误)。
message接收到服务器消息时被触发,有一个参数data,表示接收到的数据,类型可以是字符串、Buffer、ArrayBuffer。
open连接建立时被触发。

WebSocket构建实时聊天室

创建WebSocket服务器:

//1、安装ws模块 npm install ws
//2、导入ws模块
const WebSocket=require('ws');
//3、创建WebSocket服务器
const server=new WebSocket.Server({port:8080});
//4、绑定open事件
server.on('open',()=>{
    console.log('建立连接');
})
//5、绑定close事件
server.on('close',()=>{
    console.log('关闭连接')
})
//6、绑定connection事件 ws参数表示客户端 req表示客户端的请求信息
server.on('connection',(ws,req)=>{
    //6.1获取客户端的ip port
    const ip=req.connection.remoteAddress;
    const port=req.connection.remotePort;
    const clientName=ip+port;
    console.log('%s is connect',clientName);
    //6.2向客户端发送消息
    ws.send('欢迎'+clientName);
    ws.on('message',(msg)=>{
        console.log('消息:%s 来自于 %s',msg,clientName);
        //6.3把消息广播所有客户端:clients表示所有客户端
        server.clients.forEach(function (client) {
            //若某个客户端是打开的,就把消息广播给该客户
            if (client.readyState===WebSocket.OPEN){
                client.send(clientName+'---------'+msg);
            }
        })
    })
})

创建WebSocket客户端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
</head>
<body>
    <script type="text/javascript">
        var scoket;
        //若浏览器不支持WebSocket
        if (!window.WebSocket){
            window.WebSocket=window.MozWebSocket;
        }
        if (window.WebSocket){
            //创建客户端的WebSocket对象
            socket=new WebSocket('ws://localhost:8080/ws')
            //给socket绑定message事件
            socket.onmessage=function (event){
                //获取textarea标签
                let ta=document.getElementById('responseText');
                ta.value=ta.value+'\\n'+event.data;
            };
            //给socket绑定open事件 与服务器建立连接
            socket.onopen=function (event){
                let ta=document.getElementById('responseText');
                ta.value='连接开启';
            };
        }else{
            alert('您的浏览器不支持WebSocket协议');
        }
        //定义发送消息的函数
        function send(message){
            if (!window.WebSocket){
                return;
            }
            if (socket.readyState===WebSocket.OPEN){
                //向服务器发送消息
                socket.send(message);
                //清空消息输入框
                document.getElementById('msg').value='';
            }else{
                alert('连接未开启!');
            }
        }
    </script>
    <h2>WebSocket聊天室</h2>
	<!--    关闭表单的提交功能-->
    <form onsubmit="return false">
		<!--    来显示聊天室的聊天记录-->
	    <textarea id="responseText" style="height:300px;width: 500px"></textarea>
	    <br><br>
	    <input type="text" id="msg" name="message" placeholder="请输入消息"style="width: 300px"/>
	    <input type="button" value="发送消息" onclick="send(this.form.message.value)"/>
	    <input type="button" value="清空聊天记录" onclick="javascript:document.getElementById('responseText').value=''">
  </form>
</body>
</html>

服务器页面:

客户端页面:

以上是关于Node.js 网络编程(下)实现TCPUDPWebSocket的创建的主要内容,如果未能解决你的问题,请参考以下文章

node.js中实现同步操作的3种实现方法

node.js的HTTP 事务处理示例

10 款 Node.js 框架,可用于你的下一个项目

node.js 回调函数事件循环EventEmitter ar

node.js 下使用 util.inherits 来实现继承

Node.js服务器开发(下)