赞
踩
一、net模块感知:
net模块用于创建基于TCP(或IPC)通信的服务器或客户端,它是nodejs内置模块,直接使用require(‘net’)的方式引入,类似http模块。
二、创建一个tcp服务器:
//1.引入net模块 const net = require('net') //2.使用net的createServe({allowHalfOpen:false,pauseOnConnect:false},connectionListener)方法创建tcp服务,第一个参数为一个对象,其中两个属性值默认都是false;第二个参数是客户端与服务端建立连接的回调函数,且函数的默认参数表示socket端口对象。 const server = net.createServe((socket)=>{ console.log('有客户端连接了') console.log(socket) //6.设置客户端与服务端最大链接数量: server.maxConnections = 5; //7.获取客户度接入的数量: server.getConnections(function (err, count) { console.log("当前连接的客户端数量:" + count); }); //8.获取客户端传入过来的数据:socket对象的data事件可以获取客户端发送过来的数据,socket对象除了有data事件外,还有connect、end、error、timeout等事件,如: socket.on('data',(dt)=>{ const readSize = socket.bytesRead;// 获取接收到客户端数据的长度 console.log('客户端数据:'+ dt.toString()) }) //9.发送数据给客户端:socket.write()用来给客户端发送数,此方法第一个参数为发送内容必选,第二个参数为编码格式可选,回调函数为 socket.write('hello server!',() => { const writeSize = socket.bytesWritten;//获取发送数据的长度 console.log("数据发送成功且长度为:" + writeSize); }); //10.socket对象还有以下属性 socket.localPort:本地端口的地址 socket.localAddress:本地IP地址 socket.remotePort:进程端口地址 socket.remoteFamily:进程IP协议族 socket.remoteAddress:进程IP地址 }) //3.使用server服务的listen方法监听主机和端口,此方法和http服务类似,listen方法底层实际监测server的listening事件,可以使用on方法监听该事件如:server.on('listening',()=>{console.log('serve is running...')}) server.listen(3000,'127.0.0.1',()=>{ console.log('serve is running...') }) //4.net.Server其他常用事件: //4-1:connection:新的链接接入时触发,如: server.on('connection',()=>{console.log('新的接入')}) //4-2:close:服务器关闭时触发,如: server.on('close',()=>{console.log('服务器关闭了')}) //4-3:error:服务器发生错误时触发,如: server.on('error',()=>{console.log('服务器错误')}) //5.server.address()方法用于查看服务器监听的地址信息,返回一个对象,其属性有:port:TCP服务器监听的端口号、family:说明TCP服务器监听的地址是IPv6还是IPv4、address:TCP服务器监听的地址;此方法为监听客户端信息,因此应该在listen方法或listening事件的回调函数中使用。
三、创建一个tcp客户端:
//1.引入net模块: const net = require('net') //2.创建tcp客户端:net.Socket(obj)中可以传入一个obj对象,其属性分别为:fd文件描述、readable是否允许可读、writeable是否可写、allowHalfOpen:false时,tcp服务器接收到客户端发送的fin包后会回发,为true时不回发,前三个属性的默认值为false const client = new net.Socket() //3.连接tcp服务器: client.connect(3000,'127.0.0.1',()=>{ console.log('服务连接成功') //5.向tcp服务器发送数据:应该在连接到服务器后在发送数据,因此在连接到tcp服务器的回调函数中完成 client.write('hello server!') }) //4.获取服务端发送过来的数据:监听data事件: client.on('data',(dt)=>{ console.log('接收到服务端数据为:'+dt) }) //6.end事件用于监听客户端发送数据完成: client.on('end',()=>{ console.log('向服务端发送数据完成') })
四、项目中运用:
项目的基本思路,前端做交互通过tcp通信到tcp服务上
后端tcp客户端代码:
// 参数下设页路由: const express = require('express') const router = express.Router() const connection = require('../config/mysqldbconfig') const net = require('net') // 载入net模块,用于创建tcp客户端 // 设置或读取接口 router.post('/api/postparams',(request,response)=>{ try { let obj = request.body; let param; if (obj.isRead){ param = 'mod/'+obj.pjtid+'/'+obj.address+'/'+obj.read_cod }else { param = 'mod/'+obj.pjtid+'/'+obj.address+'/'+obj.cod+'/'+obj.inputval } // 查询ip地址和端口号: function getipport(){ return new Promise((rej)=>{ connection.query('SELECT * FROM system_setting',(error,result)=>{ try { if (error) { throw error }else{ rej(result[0]) } }catch(err){ console.log('查询参数下设ip地址接口错误'+err) } }) }) } async function sendparam () { let ipobj = await getipport() let da = '' //创建TCP客户端,在接口内部创建tcp客户端可以避免每次调用接口而产生的粘包问题,如果将创建tcp客户端的const client = new net.Socket()代码提到接口外面,那么当多次调用接口时就会出现调用一次接口响应多次数据的情况,经查阅资料需要数据的封包与拆包,这种情况需要tcp服务端和tcp客户端同时处理才能实现,我这个项目中tcp服务端不是我本人所写,经测试发现,将代码提到接口中,每次调用接口重新创建tcp客户端可间接地解决粘包问题 const client = new net.Socket(); client.connect(ipobj.par_port,ipobj.par_ip,() => { client.write(param) }) client.on("data", function (data) { da = data.toString() if(da === 'err'){ response.writeHead(201) response.end() } else { response.end(da) } }) client.on("error",(err)=>{ throw err }) } sendparam() }catch(err) { console.log('设置下设参数接口错误:'+err) } }) module.exports = router
前端页面主要代码:
<!-- 部分界面代码: --> <el-dialog v-model="dialogVisible" title='下设参数' width="60%"> <el-table :data="paramdata" border height='510'> <el-table-column prop="pname" label="参数名称"></el-table-column> <el-table-column prop="cod" label="编码"></el-table-column> <el-table-column label="读取/写入"> <template #default="scope"> <input v-if='scope.row.input_type === 1 ? true : false' type='text' v-model="scope.row.inputval" placeholder="输入值"> <el-select v-if='scope.row.input_type === 0 ? true : false' v-model="scope.row.inputval" placeholder="选择值"> <el-option v-for="item in scope.row.select_value" :key="item.key" :label="item.key" :value="item.val"></el-option> </el-select> </template> </el-table-column> <el-table-column fixed="right" label="操作" width="150"> <template #default="scope"> <el-button type="primary" size="small" @click='setParam(scope.row)'>下设</el-button> <el-button type="success" plain size="small" @click='getParam(scope.row)'>读取</el-button> </template> </el-table-column> </el-table> </el-dialog> //部分逻辑代码: methods: { getParam (e) { // 读取参数 e.isRead = true // 控制是读取还是下设 this.sendRequest(e) }, setParam (e) { // 下设参数 if (e.input_type === 1) { const regstr = '^[0-9]{' + (e.min_value === 0 ? 0 : e.min_value.toString().length) + ',' + e.max_value.toString().length + '}.{0,1}[0-9]{0,' + e.dotted_count + '}$' const reg = new RegExp(regstr) if (reg.test(e.inputval) && e.inputval >= e.min_value && e.inputval <= e.max_value) { this.sendRequest(e) } else { this.ElMessage({ // 表单校验失败,做出提示: message: '提示:输入的值无效!', type: 'warning' }) } } else { this.sendRequest(e) } }, async sendRequest (el) { // 调用读取或下设的接口 const postdata = await this.$http.post('/api/postparams', el) if (postdata.status !== 200) return this.ElMessage('提示:失败') this.ElMessage({ message: '提示:成功!', type: 'success' }) } }
前端项目我是用vue写的,其中参数e是从数据库中获取过来的值,另外后端接口中obj.pjtid和obj.address是从父元素传入的参数(数字和字符串,这里可忽略,不影响阅读),数据库中的值如下图:
优化:前端每点击下设或读取一次就建立一次连接的导致的性能问题:
// 参数下设页路由: const express = require('express') const router = express.Router() const connection = require('../config/mysqldbconfig') const net = require('net') // 载入net模块,用于创建tcp客户端 const client = new net.Socket() let res = {} // 获取下设参数数据 router.post('/api/getparamedata',(req,res)=>{ let reqobj = req.body; let sql = 'SELECT ps.* FROM param_sets ps INNER JOIN param_user pu ON pu.par_id = ps.id WHERE pu.user_id = "'+reqobj.uid+'" AND ps.show = 1' connection.query(sql,(error,result)=>{ try { if(error){ throw error; }else{ res.send(result) } } catch (err) { console.log('获取下设参数数据接口错误:'+err) } }) connection.query('SELECT * FROM system_setting',(error,result)=>{ try { if (error) { throw error }else{ client.connect(result[0].par_port,result[0].par_ip,()=>{ console.log('服务连接成功') }) } }catch(err){ console.log('查询参数下设ip地址接口错误'+err) } }) }) // 设置下设参数接口 router.post('/api/postparams',(request,response)=>{ res = response let obj = request.body; let param; if (obj.isRead){ param = 'mod/'+obj.pjtid+'/'+obj.address+'/'+obj.read_cod+'/' }else { param = 'mod/'+obj.pjtid+'/'+obj.address+'/'+obj.cod+'/'+obj.inputval } client.write(param) }) client.on('data',(dt)=>{ console.log('接收到服务端数据为:'+dt) res.end(dt) }) client.on('end',()=>{ console.log('向服务端发送数据完成') }) module.exports = router
提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:810665436@qq.com联系笔者删除。
笔者:苦海
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。