有段时间没有更新博客了,近来比较忙,没有顾上写博客。终于完成了一个大任务,有时间回顾一下这段时间的成果。这篇博客,先介绍和总结一下很久前的工作。TCP/IP接收数据拼包。由于时间太长很多东西记不清楚了,请见谅。
任务是某设备通过WIFI以TCP/IP的协议发送图像数据,数据按照规定的报文协议接收数据。
报文内容分为控制域(8个字节)与数据域(不定长),报文的启动字符为0628H占两个字节,接下来两个字节是报文长度(除去控制域本身之外的所有字节长度,因此加上启动字符在内的完整的报文为报文长度的值+8个字节)。控制域后面4个字节预留。数据域前2个字节为数据类型,接下来2个字节为数据内容长度,接下来2个字节为帧类型,接下来2个字节标志位标示是否有后续帧,然后是真正的图像数据内容(由于图像数据内容很大,一帧报文数据可能发不完,因此分多帧发送,后续帧标志位就标志某一帧图像数据是否发完)。
然后约定,所有数据类型按小端对齐(低字节在前。当然也可以约定大端对齐,高字节在前。)
好的,协议定好,接下来就开始发送数据和接收数据把。不过,发送数据的工作不在我这边,我只负责接收。不过,发送数据的工作跟接收数据的工作可以互相参考一下。整理一下我的任务,我需要通过TCP接收发过来的数据,识别出启动字符和报文长度,然后按报文长度的值接收报文。接收完一帧报文后,开始解包操作。由于一帧完整的图像可能分多帧报文发,所以,解包的时候需要注意是否有后续帧,数据是否完整了。
好,网上如何TCP接收数据的代码很多,先上代码。
- #include <stdio.h>
- #include<sys/socket.h>
- #include<arpa/inet.h> //inet_addr
- #include<netdb.h> //hostent
- #include <sys/types.h>
- #include <assert.h>
- #include<string.h> //strcpy
- #include<map>
- #include<vector>
- #include<fstream>
- #include<unistd.h>
- using namespace std;
- #pragma pack(push, 1)
- static int stepSize=0;
-
-
- void handleDataUint(char *dataUnit, int size)
- {
-
- }
-
- int main(int argc, char **argv)
- {
-
- int socket_desc,rcv_size;
- int err=-1;
- socklen_t optlen;
- struct sockaddr_in server;
- char server_reply[5000];
-
-
-
- socket_desc = socket(AF_INET , SOCK_STREAM , 0);
- if (socket_desc == -1)
- {
- printf("Could not create socket");
- }
- rcv_size = 4*640000;
- optlen = sizeof(rcv_size);
- err = setsockopt(socket_desc,SOL_SOCKET,SO_RCVBUF, (char *)&rcv_size, optlen);
- if(err<0){
- printf("设置接收缓冲区大小错误\n");
- }
- server.sin_addr.s_addr = inet_addr("192.168.10.2");
- server.sin_family = AF_INET;
- server.sin_port = htons( 52404 );
-
- if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
- {
- perror("connect error:");
- return 1;
- }
- printf("Connected");
-
- char recbuff[800000];
- unsigned long buffsize;
- int packetCount = 0;
-
-
- buffsize=0;
- while (1) {
-
-
- int readSize = recv(socket_desc, server_reply , sizeof(server_reply) , 0);
-
-
-
- if(readSize < 0) {
- printf("recv failed");
-
- assert(false);
- return 1;
- }
- else if (readSize == 0) {
- printf("readSize=0");
- break;
- }
- ++packetCount;
-
- memcpy(recbuff + buffsize, server_reply, readSize);
-
- buffsize += readSize;
- const int packetHeadSize = 8;
- static int expectedPacketSize = -1;
- if (expectedPacketSize == -1 && buffsize >= packetHeadSize) {
-
- unsigned char t0 = recbuff[0];
- unsigned char t1 = recbuff[1];
- assert(t0 == 0x28 && t1 == 0x06);
- if (!(t0 == 0x28 && t1 == 0x06))
- {
- return 1;
- }
-
-
- unsigned short len = 0;
- memcpy(&len, recbuff + 2, 2);
- expectedPacketSize = len + packetHeadSize;
- }
-
-
- if (expectedPacketSize != -1 && buffsize >= expectedPacketSize) {
-
-
-
- handleDataUint(recbuff + packetHeadSize, expectedPacketSize -packetHeadSize);
-
- memmove(recbuff, recbuff + expectedPacketSize, buffsize - expectedPacketSize);
- buffsize -= expectedPacketSize;
- expectedPacketSize = -1;
- }
- }
- return 0;
- }
- #pragma pack(pop)
其中,涉及到缓存区的数据处理,主要是memcpy,memmove等函数的使用,且buffsize,expectedPacketSize等数据大小的使用。buffsize不仅可以表示目前recbuff缓存区已经存入的数据大小,而且还表征了下一帧要存放的数据地址。而expectedPacketSize=-1可以用于判断某次recv接收到的数据是否完毕,是否含有报文的开头,不等于-1的时候又可以表示期望获得的完整一帧报文的数据大小。
由于TCP/IP的限制,一帧很大的报文需要分次多次发送,这样就需要将多次发送过来的包进行拼包处理,已避免粘包等情况。
以上是我用C++的拼包的代码。希望有用哈~
时间太长,很多别的东西记不住了,就先这样吧,我得去忙了。