Following the previous article with native code to write socket, now this article mainly introduces the use of GCDAsyncSocket, the subsequent will write about GCDAsyncSocket source code analysis.

GCDAsyncSocket use

  1. Import via PODpod 'CocoaAsyncSocket'
  2. Import header file#import <GCDAsyncSocket.h>
  3. Declaring variables follows the proxy
@interface ViewController ()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket *socket;
@end
Copy the code

4. Connect the socket

Pragma mark - Connects the socket- (IBAction)didClickConnectSocket:(id)sender {// create socketif(self.socket == nil) // Concurrent queue, this queue will affect the delegate callback, but inside is the synchronization function! To make sure the data is not cluttered, Socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; / / a socket connectionif(! self.socket.isConnected){ NSError *error; [self.socket connectToHost:@"127.0.0.1" onPort:8040 withTimeout:-1 error:&error];
        if (error) NSLog(@"% @",error); }}Copy the code

5. Send the MESSAGE

- (IBAction)didClickSendAction:(id)sender {
    
    NSData *data = [@"Message content sent" dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:data withTimeout:-1 tag:10086];
}
Copy the code

6. Reconnection

- (IBAction)didClickReconnectAction:(id)sender {// create socketif(self.socket == nil) self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; / / a socket connectionif(! self.socket.isConnected){ NSError *error; [self.socket connectToHost:@"127.0.0.1" onPort:8040 withTimeout:-1 error:&error];
        if (error) NSLog(@"% @",error); }}Copy the code

7. Close the socket

- (IBAction)didClickCloseAction:(id)sender {
    [self.socket disconnect];
    self.socket = nil;
}
Copy the code

GCDAsyncSocketDelegate

// connect to server - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(nonnull NSString *)host port:(uint16_t)port{NSLog(@"Connection successful: %@-- %d",host,port); // Connect successfully or receive message, must startreadOtherwise the message will not be received, // Noread[self.socket] [self.socket] [self.socketreadDataWithTimeout:-1 tag:10086]; } // connection disconnects - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{NSLog(@"Disconnect socket connection cause :%@",err); - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{NSLog(@)"Received data with tag = %ld: %ld length",tag,data.length); // Connect successfully or receive message, must startreadOtherwise the message will not be received // Noread// -1 indicates unlimited duration, tag [self.socket]readDataWithTimeout:-1 tag:10086]; } // the proxy sends a message to the server. - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{NSLog(@"%ld sent data successfully",tag);
}
Copy the code

debugging

According to the debugging method of the previous article, here is not repeated.

Sticking and subcontracting (unpacking)

Content extracted from

concept

During Socket communication, byte data is subcontracted and pasted, which is an internal optimization mechanism of sockets.

Stick package:

If the sent byte data packets are small and frequently sent, the Socket will glue the byte data into a whole packet to reduce memory consumption.

Subcontractor:

If the sent byte packet is large, the Socket subcontracts the sent byte data to reduce memory and performance consumption.

Example explanation
The current sender sent two packets with the following contents: 123456789 ABCDEFGHCopy the code

We want the receiver to receive two packets, the first packet is 123456789 and the second packet is ABCDEFGH. However, the situation of sticking packages and subcontracting is not the expected situation.

Stick package case

If two packets are sent at very short intervals, such as 0.1 seconds, the recipient will receive only one packet if the packet length is sufficient, as follows:

123456789ABCDEFGH
Copy the code
The subcontract conditions

Assuming that the maximum packet length is set to 5 bytes (the more extreme assumption is that the typical length is between 1000 and 1500), the receiver will receive 4 packets without sticky packets, as follows:

12345
6789
ABCDE
FGH
Copy the code
handling

Because there are sticky packets and subcontracting, the receiver needs to process the received data to a certain extent. There are two main problems to be solved:

  1. When sticky packets are generated, the contents of multiple packets can be retrieved from the same packet.
  2. When subcontracting occurs, parts of the previous package are retained and combined with parts of the next package.

Processing method: Add content length and data type to the packet header. 1. Send data

#pragma mark - Sends data formatting- (void)sendData:(NSData *)data dataType:(unsigned int)dataType{ NSMutableData *mData = [NSMutableData data]; Data unsigned int dataLength = 4+4+(int)data.length; NSData *lengthData = [NSData dataWithBytes:&dataLength Length :4]; [mData appendData:lengthData]; // 2. Concatenate instruction types (4~7: instruction) NSData *typeData = [NSData dataWithBytes:&dataType length:4]; [mData appendData:typeData]; [mData appendData:data]; NSLog(@"Total bytes of data sent :%ld",mData.length); [self.socket writeData:mData withTimeout:-1 tag:10086]; }Copy the code

2. Receive data

- (void)recvData:(NSData *)data{// Put it directly in the cache [self.cacheData appendData:data]; / / to get the total packet size / / the whole piece of data length (do not include the length and type) NSData * totalSizeData = [data subdataWithRange: NSMakeRange (0, 4)]. unsigned int totalSize = 0; [totalSizeData getBytes:&totalSize length:4]; Unsigned int completeSize = totalSize + 8; // Must be greater than 8 to enter the loopwhile (self.cacheData.length>8) {
        if(self.cachedata.length < completeSize) {// If the cache is no longer as long as the data we are passing, let the socket continue to receive data [self.socket]readDataWithTimeout:-1 tag:10086];
            break; } / / remove data NSData * resultData = [self. CacheData subdataWithRange: NSMakeRange (8, completeSize)]; / / processing data [self handleRecvData: resultData]; / / to empty just cache data [self cacheData replaceBytesInRange: NSMakeRange (0, completeSize) withBytes: nil length: 0]; // If the cached data length is still greater than 8, execute the method againif(self.cacheData.length > 8) { [self recvData:nil]; }}}Copy the code