iOS

来源:互联网 发布:路由访客网络什么意思 编辑:程序博客网 时间:2024/06/05 02:54

通过socket实现服务端与客户端的长连接,达到实时监听客户端状态、实时保持客户端和服务端连接,数据传输等等的目的,可能有以下几种叫法:1.长连接 2.心跳包的实现 3.单点登录(APP同一账号同时登录一个设备)。。。

因为项目遇到单点登录部分,所以本文实现的是第三点,如有其他需求,修改,调整一下代码即可...

FIRST.先说服务端的部分:

1.创建工程,如下图


2.代码结构


3.再说两个主要类,ZJJSocketService

.h 中 :


#import <Foundation/Foundation.h>

#import "GCDAsyncSocket.h"


@interface ZJJSocketService : NSObject

//开启服务

- (void)start;

@end


.m 中 :

#import "ZJJSocketService.h"

@interface ZJJSocketService ()<GCDAsyncSocketDelegate>

@property (strong,nonatomic)GCDAsyncSocket *socket;

@property (strong,nonatomic)NSMutableArray *clientSockets;//保存客户端scoket

@end

@implementation ZJJSocketService

- (NSMutableArray *)clientSockets

{

    if (_clientSockets ==nil) {

        _clientSockets = [[NSMutableArrayalloc]init];

    }

    return_clientSockets;

}


- (void)start

{

    //1.创建scoket对象

    GCDAsyncSocket *serviceScoket = [[GCDAsyncSocketalloc]initWithDelegate:selfdelegateQueue:dispatch_get_global_queue(0,0)];

    

    //2.绑定端口(9999)

    //端口任意,但遵循有效端口原则范围:0~65535,其中0~1024由系统使用或者保留端口,开发中建议使用1024以上的端口

    NSError *error =nil;

    [serviceScoket acceptOnPort:9999error:&error];

    

    //3.开启服务(实质第二步绑定端口的同时默认开启服务)

    if (error ==nil)

    {

        NSLog(@"开启成功");

    }

    else

    {

        NSLog(@"开启失败");

    }

    self.socket = serviceScoket;

}


#pragma mark GCDAsyncSocketDelegate

//连接到客户端socket

- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket

{

    //sock 服务端的socket

    //newSocket 客户端连接的socket

    NSLog(@"%@----%@",sock, newSocket);

    

    //1.保存连接的客户端socket(否则newSocket释放掉后链接会自动断开)

    [self.clientSocketsaddObject:newSocket];

    

    //连接成功服务端立即向客户端提供服务

    NSMutableString *serviceContent = [NSMutableStringstring];

    [serviceContent appendString:@"话费查询请按1\n"];

    [serviceContent appendString:@"话费充值请按2\n"];

    [serviceContent appendString:@"投诉建议请按3\n"];

    [serviceContent appendString:@"最新优惠请按4\n"];

    [serviceContent appendString:@"人工服务请按0\n"];

    [serviceContent appendString:@"无聊请按5\n"];


    [newSocket writeData:[serviceContentdataUsingEncoding:NSUTF8StringEncoding]withTimeout:-1tag:0];

    

    //2.监听客户端有没有数据上传

    //-1代表不超时

    //tag标示作用

    [newSocket readDataWithTimeout:-1tag:0];

}


//接收到客户端数据

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

{

    //1.接受到用户数据

    NSString *str = [[NSStringalloc]initWithData:dataencoding:NSUTF8StringEncoding];

    NSLog(@"服务端收到--->>%@",str);

    NSInteger code = [strintegerValue];

    NSString *responseString =nil;

    switch (code) {

        case1:

            responseString = @"您的账户余额为0,请尽快充值\n";

            break;

        case2:

            responseString = @"系统忙,请稍后重试\n";

            break;

        case3:

            responseString = @"系统忙,暂时不能接受投诉建议\n";

            break;

        case4:

            responseString = @"请上官网查看更多优惠\n";

            break;

        case5:

            responseString = @"无聊...\n";

            break;

        case0:

            responseString = @"客服忙,谢谢!\n";

            break;

        default:

            break;

    }

    //处理请求返回数据

    [sock writeData:[responseStringdataUsingEncoding:NSUTF8StringEncoding]withTimeout:-1tag:0];


    [sock readDataWithTimeout:-1tag:0];

}

@end

4.main.m 里面

#import <Foundation/Foundation.h>

#import "ZJJSocketService.h"

int main(int argc,const char * argv[]) {

    @autoreleasepool {

        // insert code here...

        NSLog(@"Hello, World!");

        //创建服务对象

        ZJJSocketService *socketSerview = [[ZJJSocketServicealloc]init];

        //开始服务

        [socketSerview start];

        //循环运行

        [[NSRunLoopmainRunLoop]run];//目的让服务器不停止

    }

    return0;

}



**************************************************************************************************

SECOND:再说客户端部分:

1.创建工程,结构如下图:



2.重点说GCDSocketManager这个类
.h 中:

#import <Foundation/Foundation.h>

#import "GCDAsyncSocket.h"


@interface GCDSocketManager : NSObject


@property(nonatomic,strong)GCDAsyncSocket *socket;


//单例

+ (instancetype)sharedSocketManager;


//连接

- (void)connectToServer;


//断开

- (void)cutOffSocket;


@end


.m 中:

#import "GCDSocketManager.h"


// 但是我用的自己电脑做测试,所以用的这个IP地址

#define SocketHost @"127.0.0.1"

// 端口是随意写的

#define SocketPort 9999


@interface GCDSocketManager()<GCDAsyncSocketDelegate>


//握手次数

@property(nonatomic,assign)NSInteger pushCount;


//断开重连定时器

@property(nonatomic,strong)NSTimer *timer;


// 检测心跳的定时器

@property (nonatomic,strong)NSTimer *heartBeatTimer;


//重连次数

@property(nonatomic,assign)NSInteger reconnectCount;


@end


@implementation GCDSocketManager

//全局访问点

+ (instancetype)sharedSocketManager {

    staticGCDSocketManager *_instance =nil;

    staticdispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        _instance = [[selfalloc]init];

    });

    return _instance;

}


//可以在这里做一些初始化操作

- (instancetype)init

{

    self = [superinit];

    if (self) {

       

    }

    returnself;

}

#pragma mark - 这里是操作发送心跳包定时器的方法

- (void)openTimer {

    [selfcloseTimer];

    if (self.heartBeatTimer ==nil || !self.heartBeatTimer) {

         self.heartBeatTimer = [NSTimerscheduledTimerWithTimeInterval:5target:selfselector:@selector(heartCheck)userInfo:nilrepeats:YES];

    }

    

}

- (void)closeTimer {

    [self.heartBeatTimerinvalidate];

    self.heartBeatTimer =nil;

}

- (void)heartCheck {

    [selfsendDataToServer];

    

}

#pragma mark 请求连接

//连接

- (void)connectToServer {

    self.pushCount =0;

    

    self.socket = [[GCDAsyncSocketalloc]initWithDelegate:selfdelegateQueue:dispatch_get_main_queue()];

    

    NSError *error =nil;

    [self.socketconnectToHost:SocketHostonPort:SocketPorterror:&error];

    

    if (error) {

        NSLog(@"SocketConnectError:%@",error);

    }

}


#pragma mark 连接成功

//连接成功的回调

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {

    NSLog(@"socket连接成功");

    [selfopenTimer];

}


//连接成功后向服务器发送数据

- (void)sendDataToServer {

    //发送数据代码省略...

    

    NSString *str =@"你好服务端,我是帅气的客户端,Kean..";

    

    NSData *jsonData = [strdataUsingEncoding:NSUTF8StringEncoding];

    

    //发送

    [self.socketwriteData:jsonDatawithTimeout:-1tag:1];

    

    //读取数据

    [self.socketreadDataWithTimeout:-1tag:200];

}


//连接成功向服务器发送数据后,服务器会有响应

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {

    

    NSString *str = [[NSStringalloc]initWithData:dataencoding:NSUTF8StringEncoding];

    

    NSLog(@"客户端收到数据---%@",str);

    

    [self.socketreadDataWithTimeout:-1tag:200];

    

    //服务器推送次数

    self.pushCount++;

    

    //在这里进行校验操作,情况分为成功和失败两种,成功的操作一般都是拉取数据

}


#pragma mark 连接失败

//连接失败的回调

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {

    NSLog(@"Socket连接失败");

    [selfcloseTimer];

    

    self.pushCount =0;

    

    NSUserDefaults *userDefaults = [NSUserDefaultsstandardUserDefaults];

    

    NSString *currentStatu = [userDefaultsvalueForKey:@"Statu"];

    

    //程序在前台才进行重连

    if ([currentStatuisEqualToString:@"foreground"]) {

        

        //重连次数

        self.reconnectCount++;

        

        //如果连接失败累加1秒重新连接减少服务器压力

        NSTimer *timer = [NSTimerscheduledTimerWithTimeInterval:1.0 *self.reconnectCounttarget:selfselector:@selector(reconnectServer)userInfo:nilrepeats:NO];

        

        self.timer = timer;

    }

}


//如果连接失败,5秒后重新连接

- (void)reconnectServer {

    

    self.pushCount =0;

    

    self.reconnectCount =0;

    

    //连接失败重新连接

    NSError *error =nil;

    [self.socketconnectToHost:SocketHostonPort:SocketPorterror:&error];

    if (error) {

        NSLog(@"SocektConnectError:%@",error);

    }

}


#pragma mark 断开连接

//切断连接

- (void)cutOffSocket {

    NSLog(@"socket断开连接");

    

    self.pushCount =0;

    

    self.reconnectCount =0;

    

    [self.timerinvalidate];

    self.timer =nil;

    

    [self.socketdisconnect];

}

@end


THIRD.配置完成,以下是后台的log

服务端:



客户端:



**********************************************结束线*********************************************

0 0
原创粉丝点击