SoFunction
Updated on 2025-04-12

Implementation method of long link of iOS WebSocket

WebSocket

WebSocket is a new protocol for HTML5. It realizes full duplex communication between the browser and the server, which can better save server resources and bandwidth and achieve real-time communication. It is built on TCP and transmits data through TCP like HTTP, but the biggest difference between it and HTTP is that WebSocket is a two-way communication protocol.

Since the project needs to create a chat room, it is necessary to maintain communication with the background, chat, and push hot messages in real time.

Currently, Facebook's SocketRocket should be the best framework for SocketRocket use. It is simple and easy to use.

use

Generally, a project can start creating a long link at a certain time after startup, and create multiple times if multiple links are needed. If only one can be encapsulated into a singleton, it will be used globally.

You can use podpod management library and add it to the podfile

pod 'SocketRocket'

Install on the current project using the command line tool CD

pod install

It can be used after importing the header file.

In order to achieve stability, my approach is to copy the SocketRocket library into the project in FaceBook. -->SocketRocket Address

1. First create a singleton class called WebSocketManager.

+(instancetype)shared;

2. Create an enum to represent the link status of WebSocket respectively

typedef NS_ENUM(NSUInteger,WebSocketConnectType){
  WebSocketDefault = 0,  //Initial status, not connected, no need to reconnect  WebSocketConnect,    //Connected  WebSocketDisconnect  //Disconnect after connection, need to reconnect};

3. Create a connection

//Create a long connection- (void)connectServer;

4. Process the results of successful connections;

-(void)webSocketDidOpen:(RMWebSocket *)webSocket; //The connection is successful callback

5. Process the result of connection failure

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;//Connection failed callback

6. Receive messages

////Receive message callbacks and you need to agree on the good message format in advance with the backend.- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(nonnull NSString *)string

7. Close the connection

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;/// Close the proxy for the connection callback

8. In order to maintain the connection status of a long link, you need to send messages to the background regularly, which is commonly known as: heartbeat packet.

A timer needs to be created to send messages at a fixed time.

9. Handling of link disconnection:

First, determine whether it is actively disconnected. If it is actively disconnected, no processing will be done.

If you do not actively disconnect, you need to do reconnect logic.

The specific code is as follows:

Code in

#import 
<foundation foundation="" h="">
 
#import ""
typedef NS_ENUM(NSUInteger,WebSocketConnectType){
  WebSocketDefault = 0, //Initial status, not connected  WebSocketConnect,   //Connected  WebSocketDisconnect  //Disconnected after connection};
@class WebSocketManager;
@protocol WebSocketManagerDelegate 
 <nsobject>
 
- (void)webSocketManagerDidReceiveMessageWithString:(NSString *)string;
@end
NS_ASSUME_NONNULL_BEGIN
@interface WebSocketManager : NSObject
@property (nonatomic, strong) RMWebSocket *webSocket;
@property(nonatomic,weak) id
 <websocketmanagerdelegate nbsp="">
  delegate;
@property (nonatomic, assign)  BOOL isConnect; //Whether to connect@property (nonatomic, assign)  WebSocketConnectType connectType;
+(instancetype)shared;
- (void)connectServer;//Create a long connection- (void)reConnectServer;//Reconnect- (void)RMWebSocketClose;//Close long connection- (void)sendDataToServer:(NSString *)data;//Send data to the server@end
NS_ASSUME_NONNULL_END
 </websocketmanagerdelegate>
 </nsobject>
</foundation>

Code in

#import ""
@interface WebSocketManager ()
<rmwebsocketdelegate>
 
@property (nonatomic, strong) NSTimer *heartBeatTimer; //Heartbeat timer@property (nonatomic, strong) NSTimer *netWorkTestingTimer; //Detect the network timer when there is no network@property (nonatomic, assign) NSTimeInterval reConnectTime; //Reconnect time@property (nonatomic, strong) NSMutableArray *sendDataArray; //Storing the data to be sent to the server@property (nonatomic, assign) BOOL isActivelyClose;  // Used to determine whether the long connection is actively closed. If the connection is actively disconnected, there is no need to execute the reconnection method in the proxy that fails.@end
@implementation WebSocketManager
+(instancetype)shared{
  static WebSocketManager *_instance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    _instance = [[self alloc]init];
  });
  return _instance;
}
- (instancetype)init
{
  self = [super init];
  if(self){
     = 0;
     = NO;
    
     = [[NSMutableArray alloc] init];
  }
  return self;
}
//Create a long connection- (void)connectServer{
   = NO;
  
   = nil;
  [ close];
  _webSocket = nil;
//   = [[RMWebSocket alloc] initWithURL:[NSURL URLWithString:@"/ws/token=88888888"]];
   = [[RMWebSocket alloc] initWithURL:[NSURL URLWithString:@"ws://:7272"]];
   = self;
  [ open];
}
- (void)sendPing:(id)sender{
  [ sendPing:nil error:NULL];
}
#pragma mark --------------------------------------------------
#pragma mark - socket delegate
/// Start Connection-(void)webSocketDidOpen:(RMWebSocket *)webSocket{
  
  NSLog(@"socket starts connecting");
   = YES;
   = WebSocketConnect;
  [self initHeartBeat];/// Begin your heartbeat  
}
///Connection failed-(void)webSocket:(RMWebSocket *)webSocket didFailWithError:(NSError *)error{
  NSLog(@"Connection failed");
   = NO;
   = WebSocketDisconnect;
  DLog(@"If the connection fails, you can automatically reconnect when disconnecting, please pay attention to the following points.");
  DLog(@"1. Judge the current network environment. If the network is disconnected, don't connect. Wait for the network to arrive and initiate a reconnection.");
  DLog(@"3. Limit the number of connections. If the connection fails, try again about 10 times.");
  
  //Judge network environment  if ( == AFNetworkReachabilityStatusNotReachable){ //No network  
    [self noNetWorkStartTestingTimer];//Open the network detection timer  }else{ //There is an Internet  
    [self reConnectServer];//If the connection fails, the connection will be reconnected  }
}
///Receive message-(void)webSocket:(RMWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string{
  
  NSLog(@"Receive message---- %@",string);
  if ([ respondsToSelector:@selector(webSocketManagerDidReceiveMessageWithString:)]) {
    [ webSocketManagerDidReceiveMessageWithString:string];
  }
}
///Close the connection-(void)webSocket:(RMWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
  
   = NO;
  if(){
     = WebSocketDefault;
    return;
  }else{
     = WebSocketDisconnect;
  }
  
  DLog(@"Connection closed, code:%ld,reason:%@,wasClean:%d",code,reason,wasClean);
  
  [self destoryHeartBeat]; //Destroy heartbeat when disconnected  
  //Judge network environment  if ( == AFNetworkReachabilityStatusNotReachable){ //No network    [self noNetWorkStartTestingTimer];//Open network detection  }else{ //There is an Internet    NSLog(@"Close the connection");
    _webSocket = nil;
    [self reConnectServer];//If the connection fails, the connection will be reconnected  }
}
///ping
-(void)webSocket:(RMWebSocket *)webSocket didReceivePong:(NSData *)pongData{
  NSLog(@"Accept pong data--> %@",pongData);
}
#pragma mark - NSTimer
//Initialize heartbeat- (void)initHeartBeat{
  //Heartbeat is not turned off  if() {
    return;
  }
  [self destoryHeartBeat];
  dispatch_main_async_safe(^{
     = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(senderheartBeat) userInfo:nil repeats:true];
    [[NSRunLoop currentRunLoop]addTimer: forMode:NSRunLoopCommonModes];
  })
  
}
//Reconnect- (void)reConnectServer{
  if( == RM_OPEN){
    return;
  }
  
  if( > 1024){ //Reconnect 10 times 2^10 = 1024     = 0;
    return;
  }
  
  WS(weakSelf);
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    if( == RM_OPEN &&  == RM_CONNECTING) {
      return;
    }
    
    [weakSelf connectServer];
    // CTHLog(@"Reconnecting...");    
    if( == 0){ //Exponential growth of reconnection time 2       = 2;
    }else{
       *= 2;
    }
  });
  
}
//Send heartbeat- (void)senderheartBeat{
  //Agree with the server what to send as a heartbeat identifier to reduce the size of the heartbeat packet as much as possible  WS(weakSelf);
  dispatch_main_async_safe(^{
    if( == RM_OPEN){
      [weakSelf sendPing:nil];
    }
  });
}
//Scheduling starts when there is no network -- for network detection- (void)noNetWorkStartTestingTimer{
  WS(weakSelf);
  dispatch_main_async_safe(^{
     = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer: forMode:NSDefaultRunLoopMode];
  });
}
//Timed detection network- (void)noNetWorkStartTesting{
  //There is an Internet  if( != AFNetworkReachabilityStatusNotReachable)
  {
    //Switch off the network detection timer    [self destoryNetWorkStartTesting];
    //Start reconnection    [self reConnectServer];
  }
}
//Cancel network detection- (void)destoryNetWorkStartTesting{
  WS(weakSelf);
  dispatch_main_async_safe(^{
    if()
    {
      [ invalidate];
       = nil;
    }
  });
}
//Cancel heartbeat- (void)destoryHeartBeat{
  WS(weakSelf);
  dispatch_main_async_safe(^{
    if()
    {
      [ invalidate];
       = nil;
    }
  });
}
//Close long connection- (void)RMWebSocketClose{
   = YES;
   = NO;
   = WebSocketDefault;
  if()
  {
    [ close];
    _webSocket = nil;
  }
  
  //Switch off the heartbeat timer  [self destoryHeartBeat];
  
  //Switch off the network detection timer  [self destoryNetWorkStartTesting];
}
//Send data to the server- (void)sendDataToServer:(NSString *)data{
  [ addObject:data];
  
  //[_webSocket sendString:data error:NULL];
  
  //No network  if ( == AFNetworkReachabilityStatusNotReachable)
  {
    //Open the network detection timer    [self noNetWorkStartTestingTimer];
  }
  else //There is an Internet  {
    if( != nil)
    {
      // Only when the long connection OPEN is enabled can the send method be adjusted, otherwise it will be Crash      if( == RM_OPEN)
      {
//        if ( > 0)
//        {
//          NSString *data = [0];
          [_webSocket sendString:data error:NULL]; //Send data//          [ removeObjectAtIndex:0];
//
//        }
      }
      else if ( == RM_CONNECTING) //Connecting      {
        DLog(@"Connecting, data will be automatically synchronized after reconnecting");
      }
      else if ( == RM_CLOSING ||  == RM_CLOSED) //Disconnect      {
        //Call the reConnectServer method to reconnect. After the connection is successful, continue to send data        [self reConnectServer];
      }
    }
    else
    {
      [self connectServer]; //Connect the server    }
  }
}
@end
</rmwebsocketdelegate>

Note

Before we send a message, that is, before calling the senderheartBeat/ sendDataToServer: method, we must determine whether the current scant is connected. If it is not the connection state, the program will crash.

When the iOS phone screen is off or returns to the home page, the link may be disconnected. My treatment here is to judge the status when returning to the screen. If it has been disconnected, reconnect.

In AppDelegate:

- (void)applicationDidBecomeActive:(UIApplication *)application {
  // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
  if ([WebSocketManager shared].connectType == WebSocketDisconnect) {
    [[WebSocketManager shared] connectServer];    
  }
}

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.