【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模式 ...
随机推荐
- PHP 文件上传注意一个地方,移动文件时要保证目标目录存在,否则报错
move_uploaded_file ( $_FILES ["file"] ["tmp_name"], "upload/" . $fileN ...
- Java 声明和访问控制(二) this关键字的访问
this可以引用本类的静态变量和实例变量,而在静态方法中不能引用实例变量(因为当静态方法加载时,实例变量还没有被定义和初始化) this不可以引用局部变量.例如方法的参数变量,以及在方法中定义的局部变 ...
- SPRING IN ACTION 第4版笔记-第二章-003-以Java形式注入Bean、@Bean的用法
1. package soundsystem; import org.springframework.context.annotation.Bean; import org.springframewo ...
- vs2015 Xamarin.Android安装
原文:vs2015 Xamarin.Android安装 Xamarin.Android 安装步骤,以vs2015为例 1,安装vs2015中的跨平台项,但是安装在国内肯定失败,因为需要到谷歌下载 当我 ...
- cocos2d-x拖动滑块控件CCControlSlider
#include "GameMusicSetting.h" bool GameMusicSetting::init() { if(!CCLayer::init()) { retur ...
- hadoop2.2编程:hadoop性能测试
<hadoop the definitive way>(third version)中的Benchmarking a Hadoop Cluster Test Cases 的class在新的 ...
- Codevs_1040_[NOIP2001]_统计单词个数_(划分型动态规划)
描述 http://codevs.cn/problem/1040/ 与Codevs_1017_乘积最大很像,都是划分型dp. 给出一个字符串和几个单词,要求将字符串划分成k段,在每一段中求共有多少单词 ...
- The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
The server is temporarily unable to service your request due to maintenance downtime or capacity pro ...
- 文本编辑器 CKEditor 用法
最新文本编辑器,FCK升级版:CKEditor.NET CKEditor.NET.dll 版本:3.6.4.0 官方网址:http://ckeditor.com/ 效果图: 配置web.c ...
- CLR Profiler 性能分析工具 (转)
原文地址:http://www.cnblogs.com/kevinlzf/archive/2010/11/12/1876066.html 下载地址:http://www.microsoft.com/e ...