CocoaAsyncSocket + Protobuf handles packet sticking and packet splitting issues, cocoaasyncsocket sticks to packets

Source: Internet
Author: User

CocoaAsyncSocket + Protobuf handles packet sticking and packet splitting issues, cocoaasyncsocket sticks to packets

In the previous article "ProtocolBuffer setup and demo for iOS", we shared the establishment of the environment. We used the famous github framework cococoaasynsocket for IM communication with the server, the data medium between the server and server is ProtoBuf. Next, we also encountered the problem of unpacking and sticking to the package during the development process. This problem was solved only after a few twists and turns. Here we will share the problem solving process!

First, describe the problem:

1. When the server sends a long amount of data, a packet received by the GCDAsyncSocket listener cannot be parsed. It has always been necessary to receive several packages and splice them into a complete package, which is called package splitting;

2. The server quickly sends multiple pieces of data. when the data is uploaded to the client side, several pieces of data are merged into a package, which is called a sticky package. Therefore, to parse these data together, you must know the length of each piece of data to cut and parse the data correctly.

 

First, use the key code to read the header bytes of each piece of data, and read the content length of this piece of data based on the header bytes. In this way, the problem of sticking packets can be solved perfectly. Because the length of the data is different, the length of the header byte will also be different. For example, the result of repeated tests here generally occupies 1 and 2 bytes, the length of the short content data header is 1, and that of the long content data header is 2. The code here refers to the source code of the objectivec version of Protobuf provided by Google.

/** Key code: Obtain the content length and header length of data: index --> header length (1-4 bytes in the header) */-(int32_t) getContentLength :( NSData *) data withHeadLength :( int32_t *) index {int8_t tmp = [self readRawByte: data headIndex: index]; if (tmp> = 0) return tmp; int32_t result = tmp & 0x7f; if (tmp = [self readRawByte: data headIndex: index]) >=0) {result | = tmp <7 ;} else {result | = (tmp & 0x7f) <7; if (tmp = [self readRawByte: data headIndex: index])> = 0) {result | = tmp <14;} else {result | = (tmp & 0x7f) <14; if (tmp = [self readRawByte: data headIndex: index])> = 0) {result | = tmp <21;} else {result | = (tmp & 0x7f) <21; result | = (tmp = [self readRawByte: data headIndex: index]) <28; if (tmp <0) {for (int I = 0; I <5; I ++) {if ([self readRawByte: data headIndex: index]> = 0) {return result;} result =-1 ;}}} return result;}/** read byte */-(int8_t) readRawByte :( NSData *) data headIndex :( int32_t *) index {if (* index> = data. length) return-1; * index = * index + 1; return (int8_t *) data. bytes) [* index-1];}

 

It solves the problem of sticking to the packet because the header occupies byte and the content length.

The complete code is as follows: the data sent from the client listening server is resolved into a custom protobuf model class to handle packet splitting and sticking issues.

/** Method for listening to message Proxy from the server */-(void) socket :( GCDAsyncSocket *) sock didReadData :( NSData *) data withTag :( long) tag {[self. receiveData appendData: data]; // The length of the header occupied by reading data and the length of the content read from the header // Verification Result: when the data is compared, the header occupies 1 byte, when the data is large, the header occupies 2 int32_t headL = 0; int32_t contentL = [self getContentLength: self. receiveData withHeadLength: & headL]; if (contentL <1) {[sock readDataWithTimeout:-1 tag: 0]; return;} // when unpacking: continue to receive the next message, until the message is received After all the packages are split, parse if (headL + contentL> self. receiveData. length) {[sock readDataWithTimeout:-1 tag: 0]; return;} // when the receiveData length is not smaller than the length of the first message, parse receiveData [self parseContentDataWithHeadLength: headL withContentLength: contentL]; [sock readDataWithTimeout:-1 tag: tag] ;}# pragma mark-private methods auxiliary method/** parse binary data: NSData --> Custom model object */-(void) parseContentDataWithHeadLength :( int32_t) headL withContentLen Parser :( int32_t) contentL {nsange range = NSMakeRange (0, headL + contentL); // The range of NSData * data = [self. receiveData subdataWithRange: range]; // The parsed data GPBCodedInputStream * inputStream = [GPBCodedInputStream streamWithData: data]; NSError * error; ChatMsg * obj = [ChatMsg region: inputStream extensionRegistry: nil error: & error]; if (! Error) {if (obj) [self saveReceiveInfo: obj]; // save and parse the correct model object [self. receiveData replaceBytesInRange: range withBytes: NULL length: 0]; // remove parsed data} if (self. receiveData. length <1) return; // for multiple messages merged when the package is attached, the message headL = 0 is cyclically recursive until all messages are parsed. contentL = [self getContentLength: self. receiveData withHeadLength: & headL]; [self parseContentDataWithHeadLength: headL withContentLength: contentL]; // continue parsing next}/** get the content length and header length of data: index --> length of the header (1-4 bytes) */-(int32_t) getContentLength :( NSData *) data withHeadLength :( int32_t *) index {int8_t tmp = [self readRawByte: data headIndex: index]; if (tmp> = 0) return tmp; int32_t result = tmp & 0x7f; if (tmp = [self readRawByte: data headIndex: index])> = 0) {result | = tmp <7;} else {result | = (tmp & 0x7f) <7; if (tmp = [self readRawByte: data headIndex: index])> = 0) {result | = tmp <14;} else {result | = (tmp & 0x7f) <14; if (tmp = [self readRawByte: data headIndex: index]) >=0) {result | = tmp <21;} else {result | = (tmp & 0x7f) <21; result | = (tmp = [self readRawByte: data headIndex: index]) <28; if (tmp <0) {for (int I = 0; I <5; I ++) {if ([self readRawByte: data headIndex: index] >=0) {return result ;}} result =-1 ;}}} return result ;} /** read byte */-(int8_t) readRawByte :( NSData *) data headIndex :( int32_t *) index {if (* index> = data. length) return-1; * index = * index + 1; return (int8_t *) data. bytes) [* index-1];}/** process parsed information */-(void) saveReceiveInfo :( ChatMsg *) obj {//...}
View Code

 

 Example demo: https://github.com/xiaotanit/Tan_ProtocolBuffer

Link: http://www.cnblogs.com/tandaxia/p/6718695.html

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.