Socket长连接学习
来源:互联网 发布:网络超市logo 编辑:程序博客网 时间:2024/06/05 17:28
使用环境:实现与服务器保持一个长连接,用于接收消息;
编译环境: 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; }}
0 0
- Socket长连接学习
- socket长连接,
- java socket 长连接
- java Socket长连接
- TCP socket 长连接
- android socket 长连接
- java socket 长连接
- socket 长连接
- Java Socket长连接
- android socket长连接
- socket长连接
- 长连接Socket
- android socket长连接
- socket 长连接 短连接
- Socket 长连接、短连接
- Socket 短连接、长连接
- 【Android学习】socket长连接,数据粘包问题
- socket长连接和socket短连接
- Linux设备管理(四)_从sysfs回到ktype
- Linux系列课程之Linux的介绍
- css-画虚线
- 【Spring】Spring MVC原理及配置详解
- 和外包公司还有一些没确认的情况
- Socket长连接学习
- thinkphp 框架的 URL_MODEL
- Java NIO使用及原理分析 (四)
- Android 屏幕适配方案
- React Native 弹性布局FlexBox
- java 环境变量 和 系统 环境变量
- iOS-静默方式发送邮件(SKPSMTPMessage)
- spring + shiro + cas 实现sso单点登录
- django 限制某个视图函数必须登录才能访问