【NodeJs】使用TCP套接字收发数据的简单实例
因为TCP协议是流协议,在收发数据的时候会有粘包的问题。本例使用自定义的SPtcp封包协议对TCP数据再进行一次封装,解决了粘包问题。
注:其性能仍有待优化。优化方向:使用TCP自带的接收窗口缓存。
- sptcp.js
/**
* script: sptcp.js
* description: 简单封包协议SPtcp类
* authors: alwu007@sina.cn
* date: 2016-04-14
*/ var util = require('util'); function SPtcp(socket) {
//解析所处的阶段
var _sp_parse_step = SPtcp.SP_PARSE_STEP.HEADER;
//接收缓存
var _sp_rcv_buf = new Buffer(0);
//包头
var _sp_header = null;
//包体
var _sp_body = null;
//套接字
this.socket = socket; //解析整包方法
function _spParseSPPacket(func){
if (_sp_rcv_buf.length >= SPtcp.SP_HEADER_LENGTH) {
//解析包头
_sp_header = {bodyLength: _sp_rcv_buf.readUInt16LE(0, true)};
//裁剪接收缓存
_sp_rcv_buf = _sp_rcv_buf.slice(SPtcp.SP_HEADER_LENGTH);
//解析包体
_sp_parse_step = SPtcp.SP_PARSE_STEP.BODY;
_spParseBody(func);
}
}; //解析包体方法
function _spParseBody(func){
if (_sp_rcv_buf.length >= _sp_header.bodyLength) {
var packet = _sp_rcv_buf.toString('utf8', 0, _sp_header.bodyLength);
util.log('['+socket.remoteAddress+']->['+socket.localAddress+'] receive: '+packet);
//裁剪接收缓存
_sp_rcv_buf = _sp_rcv_buf.slice(_sp_header.bodyLength);
//处理消息
try {
var msg = JSON.parse(packet);
func(msg);
} catch(e) {
util.log(e);
}
//清空包头和包体
_sp_header = null;
_sp_body = null;
//解析下一个包
_sp_parse_step = SPtcp.SP_PARSE_STEP.HEADER;
_spParseSPPacket(func);
}
}; //接收数据
this.spReceiveData = (data, func) => {
if (!func) func = msg => undefined;
//合并新旧数据
_sp_rcv_buf = Buffer.concat([_sp_rcv_buf, data]);
//解析处理数据
if (_sp_parse_step == SPtcp.SP_PARSE_STEP.HEADER) {
_spParseSPPacket(func);
} else if (_sp_parse_step == SPtcp.SP_PARSE_STEP.BODY) {
_spParseBody(func);
}
}; //发送数据
this.spSendData = msg => {
var packet = JSON.stringify(msg);
var body_buf = new Buffer(packet);
var head_buf = new Buffer(SPtcp.SP_HEADER_LENGTH);
head_buf.writeUInt16LE(body_buf.length);
var snd_buf = Buffer.concat([head_buf, body_buf]);
this.socket.write(snd_buf);
}; //销毁方法
this.spDestroy = () => {
delete this.socket;
};
} //包头长度,单位字节
SPtcp.SP_HEADER_LENGTH = 4;
//解析所处的阶段
SPtcp.SP_PARSE_STEP = {
HEADER: 0, //解析包头阶段
BODY: 1, //解析包体阶段
}; exports.SPtcp = SPtcp;
- spsvr.js
/**
* script: spsvr.js
* description: SPtcp服务器端
* authors: alwu007@sina.cn
* date: 2016-04-15
*/ var util = require('util');
var net = require('net');
var SPtcp = require('./sptcp').SPtcp; var server = net.createServer(client => {
util.log('client connected: ' + client.remoteAddress);
//套接字继承SPtcp
SPtcp.call(client, client);
//监听data事件
client.on('data', data => {
client.spReceiveData(data, msg => {
util.log('susl msg: ' + util.inspect(msg));
client.spSendData(msg);
});
});
//监听结束事件
client.on('end', () => {
util.log('disconnected from client: ' + client.remoteAddress);
client.spDestroy();
});
//监听错误事件
client.on('error', err => {
util.log(err);
client.end();
});
}); var listen_options = {
host: '172.16.200.26',
port: 6200,
};
util.log('listen options: ' + util.inspect(listen_options));
server.listen(listen_options, () => {
util.log('server bound');
});
- spcli.js
/**
* script: spcli.js
* description: SPtcp客户端
* authors: alwu007@sina.cn
* date: 2016-04-15
*/ var util = require('util');
var net = require('net');
var SPtcp = require('./sptcp').SPtcp; var connect_options = {
host: '172.16.200.26',
port: 6200,
localPort: 6201,
};
util.log('connect options: ' + util.inspect(connect_options));
var client = net.connect(connect_options, ()=>{
//套接字继承SPtcp
SPtcp.call(client, client);
//监听data事件
client.on('data', data => {
client.spReceiveData(data, msg => {
util.log('susl msg: ' + util.inspect(msg));
});
});
//监听结束事件
client.on('end', () => {
util.log('disconnected from server: ' + client.remoteAddress);
client.spDestroy();
});
//监听错误事件
client.on('error', err => {
util.log(err);
client.end();
});
//发送消息
for (var i=0; i<10; i++) {
var msg = {op:'test', msg:'hello, 草谷子!', times:i};
client.spSendData(msg);
}
//关闭连接
client.end();
});
优化方案1:接收缓存_sp_rcv_buf改为Buffer数组,并记录数组元素的长度和_sp_rcv_length。这样做的好处有两点,一点是不用每次收到数据就执行一次concat方法分配一块新的内存;一点是在执行concat方法时直接传入长度参数,加快该方法的执行速度。——于2016-04-16
优化方案2:将类的方法定义在prototype原型对象上,这样该类的所有实例就共用同一个方法副本,节约资源。——于2016-04-19
【NodeJs】使用TCP套接字收发数据的简单实例的更多相关文章
- Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象
一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字 server.bind() #把地址绑定到套接字,网络地址加端口 server.lis ...
- TCP套接字编程
一.套接字(socket)函数 图1给出了在一个TCP客户与服务器通信的流程.服务器首先启动,稍后某个客户启动,它试图连接到服务器.假设客户给服务器发送一个请求,服务器处理该请求,并且给客户发回一个相 ...
- 【UNIX网络编程(二)】基本TCP套接字编程函数
基于TCP客户/server程序的套接字函数图例如以下: 运行网络I/O.一个进程必须做的第一件事就是调用socket函数.指定期望的通信协议类型. #include <sys/socket.h ...
- 套接字编程相关函数(2:TCP套接字编程相关函数)
本文摘录自<UNIX网络编程 卷1>. 基本套接字函数 socket函数 为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型.其定义如下: #in ...
- <网络编程>基本TCP套接字编程
tcp提供了可靠传输,当tcp向另一端发送数据的时候,要求对端返回一个确认.如果没有接收到确认,tcp就重传数据并且等待更长时间,数次重传失败后,tcp才放弃. 建立一个tcp连接会发生如下事情: 服 ...
- TCP套接字端口复用SO_REUSEADDR
下面建立的套接字都是tcp套接字 1.进程创建监听套接字socket1,邦定一个指定端口,并接受了若干连接.那么进程创建另外一个套接口socket2,并试图邦定同一个端口时候,bind错误返回“Add ...
- 通用套接字选项和TCP套接字选项
1. 套接字选项函数原型: #include <sys/socket.h> int getsockopt(int sockfd, int level, int optname, void ...
- 【UNIX网络编程(四)】TCP套接字编程具体分析
引言: 套接字编程事实上跟进程间通信有一定的相似性,可能也正由于此.stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1> ...
- LINUX TCP套接字详细配置
提高服务器的负载能力,是一个永恒的话题.在一台服务器CPU和内存资源额定有限的情况下,最大的压榨服务器的性能,是最终的目的.要提高 Linux系统下的负载能力,可以先启用Apache的Worker模式 ...
随机推荐
- 关于BootStrap下图标的显示问题
我现在在做自己的毕业设计,用到了bootstrap的这一套css样式,说句心里话,这一套东西确实很好用,但是一个小问题足足浪费了我将近两个小时. 我的问题是:没有办法使用bootstrap下的图标(很 ...
- struts2文件下载出现Can not find a java.io.InputStream with the name的错误
今天在用struts2就行文件下载时出现如下错误: Servlet.service() for servlet default threw exception java.lang.IllegalArg ...
- svn删除目录后提交显示Item 'XXXX' is out of date解决方法
1.在要删除的目录上执行 svn 的 Delete 2.来到要删除目录的上级目录,执行更新操作. 3.找到要删除的目录,会显示冲突状态,在这个目录上执行Resolved. 4.在这个要删除的目录上 ...
- [cocos2d demo]认字小游戏
2013.9.5更新第二版 游戏分三个场景,分别为主场景,加载场景以及游戏场景,游戏场景分为背景层,逻辑层以及UI层 1.背景:旋转太阳,移动波浪,漂浮的云 2.UI层:随机生成字附带在帆船上移动,当 ...
- [译]GotW #1: Variable Initialization 续
Answer 2. 下面每行代码都做了什么? 在Q2中,我们创建了一个vector<int>且传了参数10和20到构造函数中,第一种情况下(10,20),第二种情况是{10, 20}. 它 ...
- c#4.0新特性之协变与逆变
1.C#3.0以前的协变与逆变 如果你是第一次听说这个两个词,别担心,他们其实很常见.C#4.0中的协变与逆变[1](Covariance and contravariance)有了进一步的完善,主要 ...
- POJ_3176_Cow_Bowling_(数字三角形)_(动态规划)
描述 http://poj.org/problem?id=3176 给出一个三角形,每个点可以走到它下面两个点,将所有经过的点的值加起来,问最大的和是多少. Cow Bowling Time Limi ...
- BZOJ3132: 上帝造题的七分钟
3132: 上帝造题的七分钟 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 483 Solved: 222[Submit][Status] Desc ...
- c#自动更新+安装程序的制作
一.自动更新的实现 让客户端实现自动更新,通常做法是在客户端部署一个单独的自动更新程序.主程序启动后,访问服务端,检查配置文件是否有更新版本,有更新版本就启动更新程序,由更新负责下载更新版本,并更新客 ...
- HTML5与CSS3权威指南.pdf8
第17章 与背景和边框相关的样式 与背景相关的新增属性 background-clip指定背景的显示范围 background-origin指定绘制背景图像时的起点 background-size指定 ...