TCP通讯之服务器与客户端的连接
我们的目的是在终端实现服务器和客户端的TCP通讯:
也就是说以代码来实现:
注
程序的缺点:当其中一个客户端退出,服务器就退出了,导致其他正在访问的客户端与服务器失去了连接;
相关的网站:
https://blog.youkuaiyun.com/qq_41885673/article/details/122216791
https://www.v5w.com/plc/566.html
https://www.v5w.com/js/js%e7%9f%a5%e8%af%86%e7%82%b9/433.html
https://www.youkuaiyun.com/tags/NtDaMgwsNDYwMS1ibG9n.html
https://zhuanlan.zhihu.com/p/411656922
一.第一个案例
在nodejs中安装modbus-serial库
本人使用的开发环境是Windows10下的VSCode,安装并配置好了npm、node、cnpm等工具,使用cnpm安装了modbus-serial库,可以在npm中搜到modbus-serial包以及它的介绍,
在对应的VSCode终端下使用cnpm install modbus-serial -g安装modbus-serial库
cnpm install modbus-serial -g
非甲烷总烃分析仪有三个关键参数,即总烃、CH4、NMHC的浓度,对应Modbus寄存器地址分别为:22,25,28,获取对应寄存器数据后还需要乘以0.01
使用modbus-serial库,使用Modbus TCP协议读取对应寄存器中的数据
相关nodejs代码如下:
var moment = require('moment')
// create an empty modbus client(创建一个空的 modbus 客户端)
var ModbusRTU = require("modbus-serial")
var client = new ModbusRTU();
// open connection to a tcp line(打开与 tcp 线路的连接)
// 创建Modbus TCP连接,IP是15.18.200.23,端口502
client.connectTCP("15.18.200.23", { port: 502 });
// 读取非甲烷总烃的关于总烃、甲烷、NMHC这3个寄存器(寄存器地址分别为22,25,28)中的浓度
// 每隔5秒钟读取保持寄存器的值,从寄存器地址22开始读取,读10个寄存器到data数组中
setInterval(function(){
client.readHoldingRegisters(22, 10, function(err, data){
// 获取当前时间
//moment.locale('zh-cn');
console.log("----------------------------------------------------------------------");
console.log("数据时间是:" + moment().format('YYYY年MM月DD日 HH时mm分ss秒'));
console.log("总烃的浓度是:" + data.data[0] * 0.01 + "ppmV");// 总烃浓度对应的寄存器地址为22
console.log("CH4的浓度是:" + data.data[3] * 0.01 + "ppmV"); // CH4浓度对应的寄存器地址为25
console.log("NHMC的浓度是:" + data.data[6] * 0.01 + "ppmV"); // NHMC浓度对应的寄存器地址为28
console.log("----------------------------------------------------------------------");
//console.log(data.data);
});
}, 5000);
上述代码使用了JavaScript 日期处理类库Moment.js对当前日期进行格式化,相关介绍见Moment.js-JavaScript 日期处理类库
使用很简单,比如项要获取当前的时间,按照2019年12月9日 16时07分23秒这种格式输出,代码如下:
var moment = require('moment');
var currentTime = moment().format('YYYY年MM月DD日 HH时mm分ss秒');
console.log("当前时间为:" + currentTime);
nodejs中安装moment库也很简单,一条命令搞定,如下:
cnpm install moment -g
相关代码测试输出如下:
数据时间是:2019年12月11号 16时07分56秒
总烃的浓度是:2.78ppmV
CH4的浓度是:2.07ppmV
NHMC的浓度是:0.70ppmV
数据时间是:2019年12月11号 16时08分01秒
总烃的浓度是:2.78ppmV
CH4的浓度是:2.07ppmV
NHMC的浓度是:0.70pmV
数据时间是:2019年12月11号 16时08分06秒
总烃的浓度是:2.78ppmV
CH4的浓度是:2.07ppmV
NHMC的浓度是:0.70ppmV
—————————————————😁————————————————
二.Node.js使用TCP通讯
Node.js 的 net 模块可以方便的创建TCP 服务,,以下是使用 net 模块创建的tcp 服务和客户端的一个简单例子。
(一)、创建TCP Server
var net = require('net');
var tcp_server = net.createServer(); // 创建 tcp server
var Sockets = {};
var SocketID = 1;
// 监听 端口
tcp_server.listen(5678,function (){
console.log('tcp_server listening 5678');
});
// 处理客户端连接
tcp_server.on('connection',function (socket){
console.log(socket.address());
Sockets[SocketID] =socket;
SocketID++;
DealConnect(socket)
})
tcp_server.on('error', function (){
console.log('tcp_server error!');
})
tcp_server.on('close', function () {
console.log('tcp_server close!');
})
// 处理每个客户端消息
function DealConnect(socket){
socket.on('data',function(data){
data = data.toString();
// 向所有客户端广播消息
for(var i in Sockets){
Sockets[i].write(data);
}
// socket.write(data);
console.log('received data %s',data);
})
// 客户端正常断开时执行
socket.on('close', function () {
console.log('client disconneted!');
})
// 客户端正异断开时执行
socket.on("error", function (err) {
console.log('client error disconneted!');
});
}
(二)、创建 TCP Client
var net = require('net');
// 指定连接的tcp server ip,端口
var options = {
host : '172.30.20.10',
port : 5678
}
var tcp_client = net.Socket();
// 连接 tcp server
tcp_client.connect(options,function(){
console.log('connected to Server');
tcp_client.write('I am tcp_client of node!');
})
// 接收数据
tcp_client.on('data',function(data){
console.log('received data: %s from server', data.toString());
})
tcp_client.on('end',function(){
console.log('data end!');
})
tcp_client.on('error', function () {
console.log('tcp_client error!');
})
三.Node.js 搭建TCP服务器
概述
TCP协议就是位于传输层的协议。Node.js在创建一个TCP服务器的时候使用的是net(网络)模块。
创建TCP服务
使用Node.js创建TCP服务器,首先要引用net模块,之后使用net模块的createServer方法就可以创建一个TCP服务器.
使用TCP服务器的listen方法就可以开始监听客户端的连接.
server.listen(port[,host][,backlog][,callback])
;
- port参数为需要监听的端口号,参数值为0的时候将随机分配一个端口号。
- host为服务器地址。
- backlog为等待队列的最大长度
- callback为回调函数
下面代码构建一个TCP服务器:
//引入net模块
const net=require('net');
//创建TCP服务器
let server=net.createServer(function(socket){
console.log('someone connets');
})
//监听客户端的连接
server.listen(18001,function(){
console.log('server is listening');
});
TCP服务端API
回调事件
支持如下回调事件:
- connection:当前新的链接创建时触发,回调函数的参数为socket连接对象,同net.createServer的第二个函数参数。
- close:TCP服务器关闭的时候触发,回调函数没有参数
- error:TCP服务器发生错误的时候触发,回调函数的参数为error对象
- listening:调用server.listen时触发的回调
//设置监听时的回调函数
server.on('listening',function(){
console.log('server is listening');
});
//设置关闭时的回调函数
server.on('close',function(){
console.log('sever closed');
});
//设置出错时的回调函数
server.on('error',function(){
console.log('error');
});
连接服务器的客户端数量
在创建一个TCP服务器的基础下,可以通过server.getConnection()
方法获取连接这个TCP服务器的客户端数量。这个方法是一个异步的方法,回调这个函数有两个参数:
- 第一个参数为error对象
- 第二个参数为连接TCP服务器的客户端数量。
除了获取连接数量外,也可以通过设置TCP服务器的maxConnections属性来设置这个TCP服务器最大连接数量。如果当连接的数量超过最大的数量的时候,服务器会拒绝新的连接。
在下面的代码中设置创建这个TCP服务器的最大连接数量为3:
var net=require('net');
//创建服务器
var server=net.createServer(function(socket){
console.log('someone connects');
//设置连接最大数量
server.maxConnection=3;
server.getConnections(function(err,count){
console.log('the count of clieent is'+count);
});
});
//设置监听端口
server.listen(18001,function(){
console.log('server is listening');
});
获取客户端发送的数据
createServer方法的回调函数参数是一个net.Socket对象(服务器所监听的端口对象),这个对象同样也有一个address方法,用来获取TCP服务器绑定的地址,同样也返回一个含有port、family、address属性的对象。
//监听dada事件
socket.on('data',function(data){
//打印data
console.log(data.toString());
});
socket对象除了有data事件外,还有connect,end、error、timeout等事件
发送数据给客户端
使用socket.write
方法可以使TCP服务器发送数据。这个方法只有一个必须参数,就是需要发送的数据;第二个参数为编码格式,可选,同时,可以为这个方法设置一个回调函数,当有用户连接TCP服务器的时候,将发送数据给客户端。
//发送数据
socket.write(message,function(){
var writeSize=socket.bytesWritten;
console.log(message+'has send');
console.log('the size of message is'+writeSize);
});
运行这段代码并连接TCP服务器,可以看到Telnet中收到了TCP服务器发送的数据,Telnet也可以发送数据给TCP服务器
这段代码中还用到了socket对象的bytesWritten和byteRead属性,这两个属性分别代表着发送数据的字节数和接收数据的字节数。
var net=require('net');
//创建服务器
var server=net.createServer(function(socket){
//获取地址信息
var address=server.address();
var message='client,the server address is'+JSON.stringify(address);
//发送数据
socket.write(message,function(){
var writeSize=socket.bytesWritten;
console.log(message+'has send');
console.log('the size of message is'+writeSize);
});
//监听dada事件
socket.on('data',function(data){
//打印data
console.log(data.toString());
var readSize=socket.bytesRead;
console.log('the size of data is'+readSize);
});
});
//设置监听端口
server.listen(18001,function(){
console.log('server is listening');
});
四.最终的实现(建立了一个服务器程序,两个客户端程序)
1.server.js
服务器程序:
const net = require('net');//引入net模块;
const PORT = 18001;
const HOST = '127.0.0.1';
let server = net.createServer(function (socket) { //创建一个TCP服务器(net模块的createServer方法);
console.log('someone connets(注意:有客户端在连接)');
//设置新的链接创建时的回调函数
server.on('connection', function () { //connection:新的链接创建时触发回调;
console.log('服务器正在连接');
});
//设置监听时的回调函数
server.on('listening', function () { //listening:调用server.listen时触发的回调;
console.log('server is listening');
});
//设置关闭时的回调函数
server.on('close', function () { //close:TCP服务器关闭的时候触发,回调函数没有参数;
console.log('sever closed');
});
//设置出错时的回调函数
server.on('error', function () { //error:TCP服务器发生错误的时候触发,回调函数的参数为error对象;
console.log('error');
});
server.maxConnection = 3;//设置连接最大数量,TCP服务器的最大连接数量为3
server.getConnections(function (err, count) { //通过server.getConnection()方法获取连接这个TCP服务器的客户端数量;
console.log('客户端的数量是:' + count);
});
//createServer方法的回调函数参数是一个net.Socket对象,该对象有一个address方法,用来获取TCP服务器绑定的地址,同样也返回一个含有port、family、address属性的对象。
var address = server.address(); //自动获取地址信息(IP,端口号,family等)
var message = 'client,服务器的地址是:' + JSON.stringify(address); //将服务器地址信息传给message;
//获取客户端发送的数据;
socket.on('data', function (data) { //监听dada事件;
console.log(data.toString());//打印data
});
//发送数据;可以为这个方法设置一个回调函数,当有用户连接TCP服务器的时候,将发送数据给客户端;
socket.write(message, function () { //使用socket.write方法可以使TCP服务器发送数据;第一个参数message是需要发送的数据;
var writeSize = socket.bytesWritten;
console.log(message + '已发送'); //打印"message信息已发送"
console.log('message的大小是:' + writeSize); //打印message信息的大小;
});
});
//设置监听端口
server.listen(PORT, HOST, function () {//监听客户端的连接;
console.log(`服务器已启动,运行在:http://${HOST},端口号:${PORT}`);
});
2.client1.js
客户端1程序
const net = require('net');
const HOST = '127.0.0.1';
const PORT = 18001;
const client = new net.Socket();
client.setEncoding = 'UTF-8';
//与服务器进行连接
client.connect(PORT, HOST, () => {
console.log('已连接到: ' + HOST + ':' + PORT);
// 建立连接后向服务器发送数据,服务器将收到这些数据
client.write('我是client,我来自客户端!');
});
// 为客户端添加data事件处理函数
// data是服务器发回的数据
client.on('data', data => {
console.log(data.toString());
});
// 为客户端添加error事件处理函数
client.on('error', error => {
console.log('error' + error);
client.end();
});
// 为客户端添加close事件处理函数
client.on('close', () => {
console.log('服务器端下线了');
});
3.client2.js
客户端2程序
const net = require('net');
const HOST = '127.0.0.1';
const PORT = 18001;
const client = new net.Socket();
client.setEncoding = 'UTF-8';
//与服务器进行连接
client.connect(PORT, HOST, () => {
console.log('已连接到: ' + HOST + ':' + PORT);
// 建立连接后向服务器发送数据,服务器将收到这些数据
client.write('我是client2,我来自客户端!');
});
// 为客户端添加data事件处理函数
// data是服务器发回的数据
client.on('data', data => {
console.log(data.toString());
});
// 为客户端添加error事件处理函数
client.on('error', error => {
console.log('error' + error);
client.end();
});
// 为客户端添加close事件处理函数
client.on('close', () => {
console.log('服务器端下线了');
});
五
sever端
const net = require("net");
const HOST = "127.0.0.1";
const PORT = 6868;
const server = net.createServer();
server.listen(PORT, HOST);
// 重要:双方建立链接时,会自动获得一个socket对象(std,socket描述符)
server.on("connection", (socket) => {
// 远程客户端地址
console.log(`connected:${socket.remoteAddress}:${socket.remotePort}`);
// 本地服务端地址
console.log(`local:${socket.localAddress}:${socket.localPort}`);
// 向客户端发送数据
socket.write("服:你好客户端");
// 收到客户端数据时
socket.on("data", (data) => {
console.log(`${data}`);
});
// 客户端主动断连,触发end事件
socket.on("end", (data) => {
console.log(`客户端${socket.remoteAddress}:${socket.remotePort}已断连`);
});
// 如果链接断开,write方法就无效了
setInterval(() => {
socket.write(`服:定时消息${Date.now()}`);
}, 1000);
});
console.log(`server listen on ${HOST}:${PORT}`);
client端
const net = require("net");
const HOST = "127.0.0.1";
const PORT = 6868;
const client = new net.Socket();
client.connect(PORT, HOST, () => {
console.log(`connected to:${HOST}:${PORT}`);
// 向服务端发送数据
client.write("客:你好服务端");
// 收到服务端数据时
client.on("data", (data) => {
console.log(data.toString());
client.write("客:已收到");
// client.end(); // 主动关闭此次tcp长连接
});
// 客户端主动断连,触发自己的end事件
client.on("end", () => {
console.log("链接已主动断开");
});
});