iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >iOS WebSocket长链接的实现方法
  • 855
分享到

iOS WebSocket长链接的实现方法

iOSWebSocket长链接 2022-05-17 02:05:36 855人浏览 安东尼
摘要

WebSocket websocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 tcp 之上,同 Htt

WebSocket

websocketHTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 tcp 之上,同 Http 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:WEBSocket 是一种双向通信协议.

由于项目需要创建一个聊天室,需要通过长链接,和后台保持通讯,进行聊天,并且实时进行热点消息的推送.

目前Facebook的SocketRocket应该是目前最好的关于SocketRocket使用的框架了.而且简单易用.

使用

一般一个项目在启动后的某个时机会启动创建一个长链接,如果需要多个就多次创建.如果只要一个就可以封装为一个单例,全局使用.

可以使用podpod管理库, 在podfile中加入

pod 'SocketRocket'

在使用命令行工具cd到当前工程 安装

pod install

导入头文件后即可使用.

为求稳定,我的做法是copy的FaceBook里SocketRocket库到项目里. --> SocketRocket地址

首先创建一个名为 WebSocketManager 的单例类,


+(instancetype)shared;

创建一个枚举,分别表示WebSocket的链接状态


typedef NS_ENUM(NSUInteger,WebSocketConnectType){
  WebSocketDefault = 0,  //初始状态,未连接,不需要重新连接
  WebSocketConnect,    //已连接
  WebSocketDisconnect  //连接后断开,需要重新连接
};

创建连接


//建立长连接
- (void)connectServer;

处理连接成功的结果;


-(void)webSocketDidOpen:(RMWebSocket *)webSocket; //连接成功回调

处理连接失败的结果


- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;//连接失败回调

接收消息


///接收消息回调,需要提前和后台约定好消息格式.
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(nonnull NSString *)string

关闭连接


- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;///关闭连接回调的代理

为保持长链接的连接状态,需要定时向后台发送消息,就是俗称的:心跳包.

需要创建一个定时器,固定时间发送消息.

链接断开情况处理:

首先判断是否是主动断开,如果是主动断开就不作处理.

如果不是主动断开链接,需要做重新连接的逻辑.

具体代码如下:

WebSocketManager.h 中的代码


#import 
<foundation foundation="" h="">
 
#import "RMWebSocket.h"
typedef NS_ENUM(NSUInteger,WebSocketConnectType){
  WebSocketDefault = 0, //初始状态,未连接
  WebSocketConnect,   //已连接
  WebSocketDisconnect  //连接后断开
};
@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; //是否连接
@property (nonatomic, assign)  WebSocketConnectType connectType;
+(instancetype)shared;
- (void)connectServer;//建立长连接
- (void)reConnectServer;//重新连接
- (void)RMWebSocketClose;//关闭长连接
- (void)sendDataToServer:(NSString *)data;//发送数据给服务器
@end
NS_ASSUME_NONNULL_END
 </websocketmanagerdelegate>
 </nsobject>
</foundation>

WebSocketManager.m 中的代码


#import "WebSocketManager.h"
@interface WebSocketManager ()
<rmwebsocketdelegate>
 
@property (nonatomic, strong) NSTimer *heartBeatTimer; //心跳定时器
@property (nonatomic, strong) NSTimer *netWorkTestingTimer; //没有网络的时候检测网络定时器
@property (nonatomic, assign) NSTimeInterval reConnectTime; //重连时间
@property (nonatomic, strong) NSMutableArray *sendDataArray; //存储要发送给服务端的数据
@property (nonatomic, assign) BOOL isActivelyClose;  //用于判断是否主动关闭长连接,如果是主动断开连接,连接失败的代理中,就不用执行 重新连接方法
@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){
    self.reConnectTime = 0;
    self.isActivelyClose = NO;
    
    self.sendDataArray = [[NSMutableArray alloc] init];
  }
  return self;
}
//建立长连接
- (void)connectServer{
  self.isActivelyClose = NO;
  
  self.webSocket.delegate = nil;
  [self.webSocket close];
  _webSocket = nil;
//  self.webSocket = [[RMWebSocket alloc] initWithURL:[NSURL URLWithString:@"https://dev-im-gateway.runxsports.com/ws/token=88888888"]];
  self.webSocket = [[RMWebSocket alloc] initWithURL:[NSURL URLWithString:@"ws://chat.workerman.net:7272"]];
  self.webSocket.delegate = self;
  [self.webSocket open];
}
- (void)sendPing:(id)sender{
  [self.webSocket sendPing:nil error:NULL];
}
#pragma mark --------------------------------------------------
#pragma mark - socket delegate
///开始连接
-(void)webSocketDidOpen:(RMWebSocket *)webSocket{
  
  NSLog(@"socket 开始连接");
  self.isConnect = YES;
  self.connectType = WebSocketConnect;
  [self initHeartBeat];///开始心跳
  
}
///连接失败
-(void)webSocket:(RMWebSocket *)webSocket didFailWithError:(NSError *)error{
  NSLog(@"连接失败");
  self.isConnect = NO;
  self.connectType = WebSocketDisconnect;
  DLog(@"连接失败,这里可以实现掉线自动重连,要注意以下几点");
  DLog(@"1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连");
  DLog(@"3.连接次数限制,如果连接失败了,重试10次左右就可以了");
  
  //判断网络环境
  if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable){ //没有网络
  
    [self noNetWorkStartTestingTimer];//开启网络检测定时器
  }else{ //有网络
  
    [self reConnectServer];//连接失败就重连
  }
}
///接收消息
-(void)webSocket:(RMWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string{
  
  NSLog(@"接收消息---- %@",string);
  if ([self.delegate respondsToSelector:@selector(webSocketManagerDidReceiveMessageWithString:)]) {
    [self.delegate webSocketManagerDidReceiveMessageWithString:string];
  }
}
///关闭连接
-(void)webSocket:(RMWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
  
  self.isConnect = NO;
  if(self.isActivelyClose){
    self.connectType = WebSocketDefault;
    return;
  }else{
    self.connectType = WebSocketDisconnect;
  }
  
  DLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",code,reason,wasClean);
  
  [self destoryHeartBeat]; //断开连接时销毁心跳
  
  //判断网络环境
  if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable){ //没有网络
    [self noNetWorkStartTestingTimer];//开启网络检测
  }else{ //有网络
    NSLog(@"关闭连接");
    _webSocket = nil;
    [self reConnectServer];//连接失败就重连
  }
}
///ping
-(void)webSocket:(RMWebSocket *)webSocket didReceivePong:(NSData *)pongData{
  NSLog(@"接受pong数据--> %@",pongData);
}
#pragma mark - NSTimer
//初始化心跳
- (void)initHeartBeat{
  //心跳没有被关闭
  if(self.heartBeatTimer) {
    return;
  }
  [self destoryHeartBeat];
  dispatch_main_async_safe(^{
    self.heartBeatTimer = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(senderheartBeat) userInfo:nil repeats:true];
    [[NSRunLoop currentRunLoop]addTimer:self.heartBeatTimer fORMode:NSRunLoopCommonModes];
  })
  
}
//重新连接
- (void)reConnectServer{
  if(self.webSocket.readyState == RM_OPEN){
    return;
  }
  
  if(self.reConnectTime > 1024){ //重连10次 2^10 = 1024
    self.reConnectTime = 0;
    return;
  }
  
  WS(weakSelf);
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reConnectTime *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    if(weakSelf.webSocket.readyState == RM_OPEN && weakSelf.webSocket.readyState == RM_CONNECTING) {
      return;
    }
    
    [weakSelf connectServer];
    //    CTHLog(@"正在重连......");
    
    if(weakSelf.reConnectTime == 0){ //重连时间2的指数级增长
      weakSelf.reConnectTime = 2;
    }else{
      weakSelf.reConnectTime *= 2;
    }
  });
  
}
//发送心跳
- (void)senderheartBeat{
  //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
  WS(weakSelf);
  dispatch_main_async_safe(^{
    if(weakSelf.webSocket.readyState == RM_OPEN){
      [weakSelf sendPing:nil];
    }
  });
}
//没有网络的时候开始定时 -- 用于网络检测
- (void)noNetWorkStartTestingTimer{
  WS(weakSelf);
  dispatch_main_async_safe(^{
    weakSelf.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:weakSelf.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
  });
}
//定时检测网络
- (void)noNetWorkStartTesting{
  //有网络
  if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
  {
    //关闭网络检测定时器
    [self destoryNetWorkStartTesting];
    //开始重连
    [self reConnectServer];
  }
}
//取消网络检测
- (void)destoryNetWorkStartTesting{
  WS(weakSelf);
  dispatch_main_async_safe(^{
    if(weakSelf.netWorkTestingTimer)
    {
      [weakSelf.netWorkTestingTimer invalidate];
      weakSelf.netWorkTestingTimer = nil;
    }
  });
}
//取消心跳
- (void)destoryHeartBeat{
  WS(weakSelf);
  dispatch_main_async_safe(^{
    if(weakSelf.heartBeatTimer)
    {
      [weakSelf.heartBeatTimer invalidate];
      weakSelf.heartBeatTimer = nil;
    }
  });
}
//关闭长连接
- (void)RMWebSocketClose{
  self.isActivelyClose = YES;
  self.isConnect = NO;
  self.connectType = WebSocketDefault;
  if(self.webSocket)
  {
    [self.webSocket close];
    _webSocket = nil;
  }
  
  //关闭心跳定时器
  [self destoryHeartBeat];
  
  //关闭网络检测定时器
  [self destoryNetWorkStartTesting];
}
//发送数据给服务器
- (void)sendDataToServer:(NSString *)data{
  [self.sendDataArray addObject:data];
  
  //[_webSocket sendString:data error:NULL];
  
  //没有网络
  if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable)
  {
    //开启网络检测定时器
    [self noNetWorkStartTestingTimer];
  }
  else //有网络
  {
    if(self.webSocket != nil)
    {
      // 只有长连接OPEN开启状态才能调 send 方法,不然会Crash
      if(self.webSocket.readyState == RM_OPEN)
      {
//        if (self.sendDataArray.count > 0)
//        {
//          NSString *data = self.sendDataArray[0];
          [_webSocket sendString:data error:NULL]; //发送数据
//          [self.sendDataArray removeObjectAtIndex:0];
//
//        }
      }
      else if (self.webSocket.readyState == RM_CONNECTING) //正在连接
      {
        DLog(@"正在连接中,重连后会去自动同步数据");
      }
      else if (self.webSocket.readyState == RM_CLOSING || self.webSocket.readyState == RM_CLOSED) //断开连接
      {
        //调用 reConnectServer 方法重连,连接成功后 继续发送数据
        [self reConnectServer];
      }
    }
    else
    {
      [self connectServer]; //连接服务器
    }
  }
}
@end
</rmwebsocketdelegate>

注意点

我们在发送消息之前,也就是调用  senderheartBeat/ sendDataToServer:方法之前,一定要判断当前scoket是否连接,如果不是连接状态,程序则会crash。

iOS手机屏幕息屏或者回主页的时候有可能会造成链接断开,我这边的处理是在回到屏幕的时候,判断状态,如果已经断开,就重新连接.

在 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];    
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: iOS WebSocket长链接的实现方法

本文链接: https://www.lsjlt.com/news/30191.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue+WebSocket页面实时刷新长连接的实现
    最近vue项目要做数据实时刷新,折线图每秒重画一次,数据每0.5秒刷新一次,说白了就是实时刷新,因为数据量较大,用定时器估计页面停留一会就会卡死。。。 与后台人员讨论过后决定使用h5...
    99+
    2024-04-02
  • WebSocket中怎么利用OkHttp实现长连接
    WebSocket中怎么利用OkHttp实现长连接,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。WebSocket介绍先简单介绍下WebSoc...
    99+
    2024-04-02
  • PHP如何实现web socket长链接
    本文小编为大家详细介绍“PHP如何实现web socket长链接”,内容详细,步骤清晰,细节处理妥当,希望这篇“PHP如何实现web socket长链接”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知...
    99+
    2023-07-05
  • 怎么使用PHP实现长链接
    这篇文章主要介绍了怎么使用PHP实现长链接的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么使用PHP实现长链接文章都会有所收获,下面我们一起来看看吧。长链接(Long Connection),也称为持久连接(...
    99+
    2023-07-05
  • PHP怎么实现长链接转化成新浪短链接API接口
    本篇内容主要讲解“PHP怎么实现长链接转化成新浪短链接API接口”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PHP怎么实现长链接转化成新浪短链接API接口”吧!接口地址新浪短网址接口地址:&n...
    99+
    2023-06-04
  • DataGrid中实现超链接的3种方法
    在 DataGrid 中实现超链接可以使用以下三种方法:1. 使用 Hyperlink 列- 在 XAML 中,将 DataGrid...
    99+
    2023-08-14
    DataGrid
  • 如何使用Go语言实现Websocket长连接保持
    如何使用Go语言实现Websocket长连接保持Websocket是HTML5提供的一种协议,可以实现服务器与客户端之间的双向通信。在实际开发中,很多场景需要保持长连接,以便实时推送数据或实现即时通讯功能。本文将介绍如何使用Go语言实现We...
    99+
    2023-12-14
    Go语言 websocket 长连接保持
  • 如何用PHP实现一个web socket长链接
    如何用PHP实现一个web socket长链接 简介 目前PHP实现web socket 都是使用框架集成来实现,比如hyperf,swoft,或者是安装swoole 扩展来实现websocket...
    99+
    2023-09-02
    php 开发语言 服务器 websocket
  • DataGrid中实现超链接的方法有哪些
    在DataGrid中实现超链接的方法有以下几种:1. 使用HyperlinkButton列类型:可以在DataGrid中的某一列使用...
    99+
    2023-08-11
    DataGrid
  • 微信小程序中怎么使用WebSocket实现长连接
    这篇文章主要讲解了“微信小程序中怎么使用WebSocket实现长连接”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“微信小程序中怎么使用WebSocket实现长连接”吧!项目使用的技术栈数据请...
    99+
    2023-06-26
  • PHP实现链表的方法
    这篇文章主要介绍了PHP实现链表的方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。链表链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针...
    99+
    2023-06-15
  • 聊聊Golang实现长连接的方案
    随着互联网的发展,实时通讯需求越来越高,长连接技术也变得越来越重要。Golang是一门功能强大的编程语言,其出众的并发性能和高效的垃圾回收机制使其成为了很多互联网公司的首选语言。本文将介绍Golang实现长连接通讯的方案。一、什么是长连接长...
    99+
    2023-05-14
  • iOS中UIBezierPath实现饼状图的方法
    这篇文章主要介绍iOS中UIBezierPath实现饼状图的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!首先看效果图:代码:#import <UIKit/UIKit.h> NS_...
    99+
    2023-06-14
  • 实现分布式WebSocket集群的方法
    目录1、问题起因场景描述2、系统架构图本文涉及的技术栈3、技术可行性分析WebSocketSession与HttpSession4、解决方案的演变4.1、Netty与SpringWe...
    99+
    2024-04-02
  • Vue websocket封装实现方法详解
    目录1.封装的ws.js文件2.使用方法1.封装的ws.js文件 let global_callback = null let socket = '' // 存储 WebSocket...
    99+
    2024-04-02
  • iOS简单抽屉效果的实现方法
    本文实例为大家分享了iOS实现简单抽屉效果的具体代码,供大家参考,具体内容如下 实现思路及步骤: 1、首先准备要滑动的view #warning 第一步 - (void)addChi...
    99+
    2022-11-13
    iOS 抽屉
  • Android中WebView实现点击超链接启动QQ的方法
    前言之前有次在面试的时候,面试官问了一个如何在WebView点击超链接启动类型QQ类似第三方应用,我当时的回答是用WebView与js交互可以做到。面试官听了没再说什么,应该是答案不是他期望的。今天发现原来可以这样实现,记录一下。实现思路在...
    99+
    2023-05-31
    android webview 超链接
  • springboot简单接入websocket的操作方法
    序 最近一个项目又重启了,之前支付了要手动点击已付款,所以这次想把这个不友好体验干掉。另外以后的扫码登录什么的都需要这个服务支持。之前扫码登录这块用的mqtt,时间上是直接把mqt...
    99+
    2024-04-02
  • navicat新建链接的方法
    这篇文章主要介绍navicat新建链接的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、双击桌面的Navicat图标,启动Navicat。2、下图是Navicat的主页面,可以...
    99+
    2024-04-02
  • html设置链接的方法
    小编给大家分享一下html设置链接的方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在HTML中可以通过使用标签<a>来设置超文本链接,语法如“&l...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作