长连接(OC)
来源:互联网 发布:linux添加ip地址 编辑:程序博客网 时间:2024/06/05 20:07
使用环境:实现与服务器保持一个长连接,用于接收消息;
编译环境: xcode8;
语 言:object-c;
协 议:TCP, socket;
描述:实现了app 与服务器保持一个长连接,能够及时判断出断网和重新连接网络,并且重新保持长连接;现在,只在前台的情况下,才保持长连接,后台和 app 没有打开的情况下,使用 APNs.(app 在后台的时候,长连接会很快的被系统 kill,如果不用 APNs, 也可以使用一段音乐,在后台的时候一直播放,这样就不会被 kill).
代码分为两部分, 一份是服务端, 一份是客户端, 服务端用于测试;
服务端代码:
1.项目中导入了GCDAsyncSocket这个类;(该类不做描述,请自行度娘);
2.自己创建一个类ServerListener,用于监听;
————————————–ServerListener.h————————————–
#import <Foundation/Foundation.h>@interface ServerListener : NSObject//开始服务-(void)start;//停止服务-(void)stop;@end
————————————–ServerListener.m————————————–
#import "ServerListener.h"#import "GCDAsyncSocket.h"//导入头文件@interface ServerListener()<GCDAsyncSocketDelegate>@property(nonatomic,strong)GCDAsyncSocket *serverSocket;//用来记录创建的 socket 对象@property(nonatomic,strong)NSMutableArray *clientSockets;//用来保存所有的 socket 对象数组@end@implementation ServerListener//懒加载-(NSMutableArray *)clientSockets{ if (!_clientSockets) { _clientSockets = [NSMutableArray array]; } return _clientSockets;}//开启服务器-(void)start{ // 创建服务端的Socket对象 self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; // 绑定端口并监听 NSError *err = nil; // 1.端口自己给,建议1024以上 [self.serverSocket acceptOnPort:5288 error:&err]; if (err) { NSLog(@"端口号被占用%@",err); }else{ NSLog(@"端口号绑定成功,服务开启"); }}//停止监听-(void)stop{ [self.serverSocket disconnect];}#pragma mark - socket的代理方法#pragma mark 有客户端请求连接到服务器-(void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{ // 1.存储客户端的socket对象 [self.clientSockets addObject:clientSocket]; NSLog(@"当前有%ld个连接的客户端 %@",self.clientSockets.count,clientSocket); // 2.开启读取数据 //注意:没读取一次,就要调用下边方法一次; [clientSocket readDataWithTimeout:-1 tag:0];}/** * 读取数据之前,调用客户端Socket对象的readDataWithTimeout方法 */#pragma mark 读取到客户端的数据-(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{ // 1.转字符串 NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // 开启读取数据,为了下次能接收到数据 [clientSocket readDataWithTimeout:-1 tag:0]; // 2.处理请求数据 // 遍历所有客户端连接对象,将数据转发给其它的客户端 for (GCDAsyncSocket *socket in self.clientSockets) { if (clientSocket != socket) { //接收到消息,转发到客户端去,消息只发送给其他客户端; [socket writeData:data withTimeout:-1 tag:0]; } }}@end
3.在 main.m 中开启服务器;
//在 main.m中开启服务器#import <Foundation/Foundation.h>#import "ServerListener.h"int main(int argc, const char * argv[]) { @autoreleasepool { ServerListener *server = [[ServerListener alloc] init]; //开启服务器 [server start]; //开启主线程运行循环,保证程序不停止 [[NSRunLoop mainRunLoop] run]; } return 0;}//注意:当开启服务器之后,可以在命令终端中输入: telnet ip port//ip 是服务器的 ip,连接上服务器;
客户端代码:
注意:因为长连接需要在 app打开的时候就开启, 所以在AppDelegate中实现相应的代码;
1.导入GCDAsyncSocket类, 网络监测类(ToolOperation);
2.在AppDelegate中实现相应的代码;
—————————————–AppDelegate—————————————–
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {#pragma 每次进入app,开启长连接,同时开启(心跳)-lzp if ([ToolOperation NetWorkCheck]) { if (self.clientSocket == nil) { // 1.创建客户端Socket对象 GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; // 2.连接服务器 //HOST:服务器地址 //PORT: 服务器接口 NSError *err = nil; [clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //保存客服端对象 self.clientSocket = clientSocket; }else{ // 2.连接服务器 NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } } } if (self.daojishiTimer == nil) { //开启心跳 self.daojishiCount = 0; self.NetWorkCount = 0; self.daojishiTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES]; } return YES;}- (void)applicationDidEnterBackground:(UIApplication *)application {#pragma 当进入后台的时候,取消长连接,并且停止(心跳)-lzp [self.daojishiTimer invalidate]; self.daojishiTimer = nil; [self.clientSocket disconnect];//关闭长连接}- (void)applicationWillEnterForeground:(UIApplication *)application {#pragma 当回到前台的时候,恢复长连接,开启(心跳)-lzp if (self.clientSocket == nil) { // 1.创建客户端Socket对象 GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)]; // 2.连接服务器 NSError *err = nil; [clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } self.clientSocket = clientSocket; }else{ // 2.连接服务器 NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } } if (self.daojishiTimer == nil) { //开启心跳 self.daojishiCount = 0;//用来记录每隔 count 秒,请求一次长连接; self.NetWorkCount = 0;//用来记录断网重新联网,请求一次长连接一次后的时间 self.daojishiTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES]; }}
————————————-socket的代理方法————————————————
#pragma mark - socket的代理方法-lzp-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{// NSLog(@"与服务器连接成功");#pragma 这里用于长连接保持的验证;;-lzp NSString * WriteStr = @"token"; NSData * WriteData = [WriteStr dataUsingEncoding:NSUTF8StringEncoding]; [self.clientSocket writeData:WriteData withTimeout:30 tag:0]; // 连接成功,设置读取数据//每次读完都要调用下边的方法 [self.clientSocket readDataWithTimeout:-1 tag:0];}#pragma (心跳)方法-lzp-(void)timerFireMethod:(NSTimer *)theTimer{ NSInteger timeCount = 20; self.daojishiCount = self.daojishiCount + 1;// NSLog(@"%ld",(long)self.daojishiCount); if (self.daojishiCount == timeCount && [ToolOperation NetWorkCheck] && !self.clientSocket.isConnected) { NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求 }else if ([ToolOperation NetWorkCheck] && self.daojishiCount != timeCount && !self.clientSocket.isConnected){ self.NetWorkCount = self.NetWorkCount + 1; if (!self.isSend) { self.isSend = YES; NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求 }else if (self.daojishiCount == timeCount){ NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求; } }else if (![ToolOperation NetWorkCheck]){ //无网络 self.isSend = NO; } if (self.daojishiCount == timeCount) { self.daojishiCount = 0; } if (self.NetWorkCount == timeCount) { self.NetWorkCount = 0; }}#pragma 接收数据-lzp-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ //1.data 转 string NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",readStr); // 每次读取完数据,都要调用下面的方式 [self.clientSocket readDataWithTimeout:-1 tag:0];}
——————————————(心跳)方法—————————————-
#pragma (心跳)方法-lzp-(void)timerFireMethod:(NSTimer *)theTimer{ NSInteger timeCount = 20; self.daojishiCount = self.daojishiCount + 1; //当每隔 time count 秒,并且有网和没有连接的时候才会重新请求长连接; if (self.daojishiCount == timeCount && [ToolOperation NetWorkCheck] && !self.clientSocket.isConnected) { NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求 }else if ([ToolOperation NetWorkCheck] && self.daojishiCount != timeCount && !self.clientSocket.isConnected){ //这种情况是,当网络突然中断,再有网的时候,立即请求一次长连接 self.NetWorkCount = self.NetWorkCount + 1; if (!self.isSend) {//如果已经发过了网络请求, 就等下一个 timecount 才会请求长连接 self.isSend = YES; NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求 }else if (self.daojishiCount == timeCount){ NSError *err = nil; [self.clientSocket connectToHost:HOST onPort:PORT error:&err]; if (err) { //连接错误 } //连接不成功--有网--发送了一次请求; } }else if (![ToolOperation NetWorkCheck]){ //无网络 self.isSend = NO; } if (self.daojishiCount == timeCount) { self.daojishiCount = 0; } if (self.NetWorkCount == timeCount) { self.NetWorkCount = 0; }}
1 0
- 长连接(OC)
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接
- 长连接&短连接
- 长连接短连接
- 长连接 短连接
- 长连接&&短连接
- 长连接 短连接
- 轮训、长轮训、长连接
- http 长连接 ++++++++++++++++++ 手动维持 长连接
- FAL behavior on 11.2 (文档 ID 1394472.1)
- 活该
- 7.3.1 Establishing a Backup Policy
- 人耳对不同频段声音的感受
- 出现“adb不是内部或外部命令,也不是可运行的程序或批量文件。”
- 长连接(OC)
- css 去掉两个图片之间间隔
- Windows下配置PHPUnit(pear已弃用,使用phpunit.phar)
- JavaScript鼠标移动到div显示图片
- 如何在VS2013上配置MPIR库(单个项目中)
- ios 10 开发-使用 Extension(Intents Extension和Intents UI Extens)创建SiriKit功能开发
- 安卓开发复习笔记——Fragment+FragmentTabHost组件(实现新浪微
- 分享下多年积累的对JAVA程序员成长之路的总结 --本文内容转自JAVA吧
- mysql 练习题