Explanation of the use of AsyncSocket libraries in iOS applications to handle Socket communication

  • 2020-06-03 08:28:32
  • OfStack

socket enables instant messaging like QQ. The client and server need to establish a long connection and, in the case of a long connection, send a message. The client can send a heartbeat packet to detect a long connection.

socket is used in iOS development, and AsyncSocket is generally used in the third library, which I have to admit is quite powerful. Download from CocoaAsyncSocket
.

features

The AsyncSocket class supports TCP.
AsyncUdpSocket is for UDP.
AsyncSocket is an TCP/IP socket network library that encapsulates CFSocket and CFSteam. It provides asynchronous operations, complete delegate-based support for native cocoa classes. It has the following characteristics:

Non-blocking read and write to the queue, and optionally timeout. You can call it to read and write, and it will tell you when it's done. Automatic socket reception. If you call it to receive connections, it will start a new instance for each connection and, of course, close those connections immediately. Delegate (delegate) support. Errors, connections, receives, full reads, full writes, progress, and disconnections can all be invoked through delegate mode. Based on run loop, not threads. You can use it in the main thread or worker thread, but you don't have to. It calls the delegate method asynchronously, using NSRunLoop. Delegate methods include arguments to socket that allow you to distinguish between multiple instances. Self contained in 1 class. You don't need to manipulate streams or socket, this class does it all for you. Support for TCP streams based on IPV4 and IPV6.

AsyncUdpSocket is the UDP/IP socket network library, packaged from CFSocket. It works much like the TCP version, except that it works with UDP. It includes non-blocking queue-based send and receive operations, full delegate support, ES47en-based, self-contained classes, and support for IPV4 and IPV6.

AsyncSocket can be used to do a layer of encapsulation, according to the requirements of several interfaces out. For example: connect, disconnect, send a message, and so on. There are also messages received, which can be sent through notifications, agents, block, and so on.

Generally speaking, a user only needs to establish a long connection to socket, so singleton classes can be used for ease of use.

Define a single-column class: LGSocketServe

LGSocketServe.h


//
//  LGSocketServe.h
//  AsyncSocketDemo
// #import <Foundation/Foundation.h>
#import "AsyncSocket.h" @interface LGSocketServe : NSObject<AsyncSocketDelegate> + (LGSocketServe *)sharedSocketServe;
@end

LGSocketServe.m

//
//  LGSocketServe.m
//  AsyncSocketDemo
// #import "LGSocketServe.h" @implementation LGSocketServe
static LGSocketServe *socketServe = nil; #pragma mark public static methods
+ (LGSocketServe *)sharedSocketServe {
    @synchronized(self) {
        if(socketServe == nil) {
            socketServe = [[[self class] alloc] init];
        }
    }
    return socketServe;
}
+(id)allocWithZone:(NSZone *)zone
{
    @synchronized(self)
    {
        if (socketServe == nil)
        {
            socketServe = [super allocWithZone:zone];
            return socketServe;
        }
    }
    return nil;
}  
@end

Establish the socket long connection

LGSocketServe.h


@property (nonatomic, strong) AsyncSocket         *socket;       // socket //  socket The connection
- (void)startConnectSocket;
LGSocketServe.m // Set your own
#define HOST @"192.168.0.1"
#define PORT 8080 // Setting connection timeout
#define TIME_OUT 20 - (void)startConnectSocket
{
    self.socket = [[AsyncSocket alloc] initWithDelegate:self];
    [self.socket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
    if ( ![self SocketOpen:HOST port:PORT] )
    {     } } - (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port
{     if (![self.socket isConnected])
    {
        NSError *error = nil;
        [self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error];
    }     return 0;
}

Macro definition 1 HOST, PORT, TIME_OUT, startConnectSocket method. This time to set 1 AsyncSocket agent AsyncSocketDelegate. When the long connection is successful, it will call:

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    // This is the asynchronous return of connection success,
    NSLog(@"didConnectToHost");
}

The heartbeat

LGSocketServe.h


@property (nonatomic, retain) NSTimer             *heartTimer;   // Heart meter
LGSocketServe.m - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    // This is the asynchronous return of connection success,
    NSLog(@"didConnectToHost");     // Long connections are detected by sending messages continuously through a timer
    self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];
    [self.heartTimer fire];
} // The heartbeat connection
-(void)checkLongConnectByServe{     // Sends a fixed but message to the server to detect a long connection
    NSString *longConnect = @"connect is here";
    NSData   *data  = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:data withTimeout:1 tag:1];
}

In the callback method with a successful connection, start the timer and send a fixed message to the server every 2 seconds to detect the long connection. (This is based on the needs of the server)

disconnect

1. The user disconnects manually

LGSocketServe.h


// Disconnect the socket The connection
-(void)cutOffSocket;
LGSocketServe.m -(void)cutOffSocket
{
    self.socket.userData = SocketOfflineByUser;
    [self.socket disconnect];
}

cutOffSocket is when the user disconnects and does not attempt to reconnect.

2, wifi disconnect, socket disconnect

LGSocketServe.m


- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{     NSLog(@" willDisconnectWithError %ld   err = %@",sock.userData,[err description]);
    if (err.code == 57) {
        self.socket.userData = SocketOfflineByWifiCut;
    } }

When wifi is disconnected, it will call back onSocket:willDisconnectWithError: method, ES118en. code == 57, setting self. socket. userData = SocketOfflineByWifiCut.

Reconnect the

When socket is disconnected, it will be called back:

LGSocketServe.m


- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{     NSLog(@"7878 sorry the connect is failure %ld",sock.userData);     if (sock.userData == SocketOfflineByServer) {
        // The server is disconnected and reconnected
        [self startConnectSocket];
    }
    else if (sock.userData == SocketOfflineByUser) {         // If disconnected by the user, do not reconnect
        return;
    }else if (sock.userData == SocketOfflineByWifiCut) {         // wifi Disconnect, do not reconnect
        return;
    } }

In the onSocketDidDisconnect callback method, self.socket.userData is used to determine if a reconnection is needed.

Send a message

LGSocketServe.h


// Send a message
- (void)sendMessage:(id)message;
LGSocketServe.m // Set write timeout -1 Indicates that a timeout will not be used
#define WRITE_TIME_OUT -1 - (void)sendMessage:(id)message
{
    // Send data like a server
    NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];
} // Call back after sending the message successfully
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{ }

After sending the message successfully, onSocket:didWriteDataWithTag: is called, in which the message can be read.

Receive a message

LGSocketServe.m


// Set read timeout -1 Indicates that a timeout will not be used
#define READ_TIME_OUT -1 #define MAX_BUFFER 1024 // Call back after sending the message successfully
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    // Read the message
    [self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
} // Call back after receiving the message successfully
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // When the server returns a large amount of message data, it may return multiple times. So when you read the message, set MAX_BUFFER Represents the maximum number of reads per time, when data.length < MAX_BUFFER We think it might be acceptance 1 A complete message before parsing
    if( data.length < MAX_BUFFER )
    {
        // Receive result parsing ...
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        NSLog(@"%@",dic);
        // The parsed messages can be sent through notifications, proxies, block Come out,     }
    [self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];

The message is accepted, parsed, and then passed out via notification, proxy, block, and so on. In the onSocket:didReadData:withTag: callback method, messages need to be read continuously because the server will return many times due to the large amount of data. So we need to define an MAX_BUFFER macro to indicate the maximum number of reads per session. When data length < We think it is possible to receive a complete message before parsing it.

Error handling

LGSocketServe.m


//
//  LGSocketServe.m
//  AsyncSocketDemo
// #import "LGSocketServe.h" @implementation LGSocketServe
static LGSocketServe *socketServe = nil; #pragma mark public static methods
+ (LGSocketServe *)sharedSocketServe {
    @synchronized(self) {
        if(socketServe == nil) {
            socketServe = [[[self class] alloc] init];
        }
    }
    return socketServe;
}
+(id)allocWithZone:(NSZone *)zone
{
    @synchronized(self)
    {
        if (socketServe == nil)
        {
            socketServe = [super allocWithZone:zone];
            return socketServe;
        }
    }
    return nil;
}  
@end
0
An socket error will call back the onSocket:willDisconnectWithError: method to read the unread buffer via unreadData.

use

Import # import "LGSocketServe. h"


 LGSocketServe *socketServe = [LGSocketServe sharedSocketServe];
//socket Disconnect the connection before you connect so you don't connect before socket The connection did not break causing a flit back
[socketServe cutOffSocket];
socketServe.socket.userData = SocketOfflineByServer;
[socketServe startConnectSocket]; // Send a message @"hello world" Just an example, depending on the message format on the server
[socketServe sendMessage:@"hello world"];


Related articles: