TCP客户端
TCP通信客户端-解决数据包接收不全的过程
背景:5个串口条码枪,通过MOXA Nport系列转换器,以TCPServer的形式推送扫描到的条码到客户端。5个条码枪均位于流水线上方的支架上,流水线货物流过的瞬间条码枪完成箱中12个或者4个货物的扫描(12个一箱时启动三个条码枪,4个一箱时启动两个条码枪),并推送出去。
问题:开发TCP客户端接收并解析条码
由于条码枪半自动模式下,自动识别条码时前排的货物条码总是漏掉,因此采用客户端循环发送读取命令的方式;
条码枪扫描推送的条码没有开始符和数据包长度,只有
0x10
,0x13
两个结束符。
这是开发前连接的需求和信息。
TCP客户端版本一
本着不重复造轮子的思路,直接照搬之前局域网PLC通信的TCP实现,稍加改造,就ok了。
public void ReceiveServerTcpMsg()
{
var buffer = new byte[4096];
while (!BExit)
{
try
{
if (_socket != null && _socket.Connected)
{
if (_socket.Poll(-1, SelectMode.SelectRead))
{
if (_socket.Available > 0)
{
//接收条码
var recLength=_socket.ReceiveFrom(buffer, SocketFlags.None, ref _serverEndPoint);
//解析条码
var barcode = GetBarCodeFromAscii(buffer);
//后续业务逻辑处理
}
}
}
}
catch (Exception e)
{
if (EquipMessage != null)
{
EquipMessage(EquipName, _barcodeNo, 0, string.Format("ReceiveServerTcpMsg异常。ErrorCode={0}", e.Message));
}
}
}
}
写好了,拿个条码枪测试一下。
扫一个条码ok,扫一个条码ok,连扫几个条码问题出现了:
我扫的条码是6位的,但是总会出现2位或者4位的条码。
怎么会这样呢?这个实现在与PLC的通讯过程中是没有问题的呀,这里数据怎么会不全。当时手边没有资料,按照函数的说明一致在调整
_socket.Available>0
因为发送的可访问的字节数大于0就读,可能没有发送完毕,因此出现断码,于是调整为
_socket.Available>5
问题暂时解决了,但是真正使用时的条码也不是6位,而且即使这样只是断码的情况还是偶尔会出现。但是有几个疑问:
TCP一个数据包不是发送的很快吗?为什么会出现数据包不全的情况?
晚上开始查阅资料,发现了一些有用的东西:
- TCP底层存在着分包机制,网络传输过程中可能会出现粘包;
- 串口转网口通信是典型按照一定频率发送数据的;
- 更详细一点解释可以参照这两篇博客C# Socket Networkstream接收数据不全和关于串口接收并解析数据
基于以上信息可以确认
- TCP数据传输是基于流的,一条完整的数据可能分包后需要发送多次,也会由于网路堵塞多个数据包粘在一起;
- TCP通讯数据包,客户端需要根据通讯协议约定的数据包头部、尾部、长度等信息,重新组合成一条完成的数据。
于是第二个版本的客户端就诞生了。
//接收函数
public void ReceiveServerTcpMsg()
{
var buffer = new byte[4096];
while (!BExit)
{
try
{
if (_socket != null && _socket.Connected)
{
if (_socket.Poll(-1, SelectMode.SelectRead))
{
if (_socket.Available > 5)
{
var recLength=_socket.ReceiveFrom(buffer, SocketFlags.None, ref _serverEndPoint);
if(recLength<=0)continue;
//接收字节放入缓冲区
var recStr=new StringBuilder();
//因为出队列和入队列是两个线程,所以需要加锁
//接收线程只负责把数据放在缓冲队列
lock (_barcodeBuffer)
{
for (var i = 0; i < recLength; i++)
{
_barcodeBuffer.Enqueue(buffer[i]);
recStr.Append(buffer[i]);
}
}
}
}
}
}
catch (Exception e)
{
if (EquipMessage != null)
{
EquipMessage(EquipName, _barcodeNo, 0, string.Format("ReceiveServerTcpMsg异常。ErrorCode={0}", e.Message));
}
}
}
}
处理函数:
private void receiveTimer_Elapsed(object sender, ElapsedEventArgs e)
{
//没有收到数据
if (_barcodeBuffer.Count <= 0) return;
var buffer = new byte[1024];
//加锁
lock (_barcodeBuffer)
{
//剔除开头空字符
while (_barcodeBuffer.Peek().Equals(10) || _barcodeBuffer.Peek().Equals(13))
{
_barcodeBuffer.Dequeue();
}
if (_barcodeBuffer.Count <= 0) return;
var length = 0;
//没有收到结束符不读取缓冲区
if (!_barcodeBuffer.Contains(10) || !_barcodeBuffer.Contains(13)) return;
byte item;
while (!(item = _barcodeBuffer.Dequeue()).Equals(10))
{
buffer[length] = item;
length++;
}
buffer[length] = item;
//条码解析
}
}
//处理线程定时读取数据和处理数据
_receiveTimer = new Timer
{
Interval = 100,
Enabled = false,
AutoReset = true
};
_receiveTimer.Elapsed += receiveTimer_Elapsed;
//接收线程负责接收数据
new Thread(ReceiveServerTcpMsg).Start();
ok,这个版本无论是在多个条码枪同时工作还是提高条码枪扫描频率情况下都可以正常工作。这算是基本满足现有的需求了吧。
但是在后续查阅了解到还有几个问题有待解决:
- Lock锁性能太差,.net4.0提供有异步队列,是线程安全的,而且锁的性能损耗更小(本人没有使用是由于本项目基于.net3.5);
- 另外是一个业务问题:由于流水线速度、条码枪触发时机等问题,货物存在漏读的现象。
这是第一篇博客,拖了这么长时间终于写完了。
刚开始准备写的时候,感觉要写的东西挺多的,但是由于选择写博客的客户端和工作上的事情耽搁了。导致现在感觉怎么写都有点干巴巴的,好吧万事开头难,我这也算开头了。
TCP客户端的更多相关文章
- Python TCP客户端
import socket target_host="www.baidu.com" target_port=80 # 建立一个socket对象 client=socket.sock ...
- Java网络编程(TCP客户端)
TCP传输:两个端点建立连接后会有一个传输数据的通道,这个通道就称为流,而且是建立在网络基础上的流,之为socket流,该流中既可以读取也可以写入. TCP的两个端点:一个客户端:ServerSock ...
- 【实验 1-1】编写一个简单的 TCP 服务器和 TCP 客户端程序。程序均为控制台程序窗口。
在新建的 C++源文件中编写如下代码. 1.TCP 服务器端#include<winsock2.h> //包含头文件#include<stdio.h>#include<w ...
- 【RL-TCPnet网络教程】第14章 RL-TCPnet之TCP客户端
第14章 RL-TCPnet之TCP客户端 本章节为大家讲解RL-TCPnet的TCP客户端实现,学习本章节前,务必要优先学习第12章TCP传输控制协议基础知识.有了这些基础知识之后,再搞本 ...
- UDP广播 与 TCP客户端 --服务端
随着倒计时的响声,自觉无心工作,只想为祖国庆生. 最近有遇到过这样一个问题,将摄像头识别的行人,车辆实时显示在客户端中.有提供接口,会以Json的数据的形式将实时将识别的对象进行Post提交.所以我们 ...
- 10-51单片机ESP8266学习-AT指令(ESP8266连接路由器,建立TCP服务器,分别和C#TCP客户端和AndroidTCP客户端通信+花生壳远程通信)
http://www.cnblogs.com/yangfengwu/p/8871464.html 先把源码和资料链接放到这里 源码链接:https://pan.baidu.com/s/1wT8KAOI ...
- android 之TCP客户端编程
补充,由于这篇文章是自己入门的时候写的,随着Android系统的升级可能有发送需要在任务 中进行,如有问题请百度 thread 或者看下面链接的文章 https://www.cnblogs.com/y ...
- TCP客户端图片上传服务端保存本地示例
//TCP客户端public class TCPClient { public static void main(String[] args)throws IOException { Socket s ...
- tcp客户端封装
1.头文件 #ifndef TCPCLIENT_H #define TCPCLIENT_H #include <QTcpSocket> class TcpClient : public Q ...
随机推荐
- 【RL-TCPnet网络教程】第18章 BSD Sockets基础知识
第18章 BSD Sockets基础知识 本章节为大家讲解BSD Sockets,需要大家对BSD Sockets有个基础的认识,方便后面章节Socket实战操作. (本章的知识点主要整理自 ...
- 体验一把做黑客的感觉-IPC$入侵之远程控制
前言 一看你就是看标题进来的,我可不是标题党啊,大家往下看吧,本文章主要介绍了利用IPC共享漏洞上传并执行木马. 基础知识 一.什么是IPC 进程间通信(IPC,Inter-Process Commu ...
- Angularjs中的缓存以及缓存清理
写在最前面:这篇博文是2篇文章组成,详细介绍了Angularjs中的缓存以及缓存清理,文章由上海尚学堂转载过来,欢迎大家阅读和评论.转载请注明出处,谢谢! 一个缓存就是一个组件,它可以透明地储存数据, ...
- Android 音视频开发(七): 音视频录制流程总结
在前面我们学习和使用了AudioRecord.AudioTrack.Camera.MediaExtractor.MediaMuxer API.MediaCodec. 学习和使用了上述的API之后,相信 ...
- Web前端-JavaScript基础教程上
Web前端-JavaScript基础教程 将放入菜单栏中,便于阅读! JavaScript是web前端开发的编程语言,大多数网站都使用到了JavaScript,所以我们要进行学习,JavaScript ...
- [Swift]LeetCode47. 全排列 II | Permutations II
Given a collection of numbers that might contain duplicates, return all possible unique permutations ...
- [Swift]LeetCode186. 翻转字符串中的单词 II $ Reverse Words in a String II
Given an input string, reverse the string word by word. A word is defined as a sequence of non-space ...
- [Swift]LeetCode377. 组合总和 Ⅳ | Combination Sum IV
Given an integer array with all positive numbers and no duplicates, find the number of possible comb ...
- ASP.NET Core 2.1 Web API + Identity Server 4 + Angular 6 + Angular Material 实战小项目视频
视频简介 ASP.NET Core Web API + Angular 6的教学视频 我是后端开发人员, 前端的Angular部分讲的比较差一些, 可以直接看代码!!!! 这是一个小项目的实战视频, ...
- BBS论坛(二十四)
24.1.编辑板块 cms/js/banners.js $(function () { $('.edit-board-btn').click(function () { var self = $(th ...