通过socket实现服务端与客户端的长连接,达到实时监听客户端状态、实时保持客户端和服务端连接,数据传输等等的目的,可能有以下几种叫法:1.长连接 2.心跳包的实现 3.单点登录(APP同一账号同时登录一个设备)。。。
因为项目遇到单点登录部分,所以本文实现的是第三点,如有其他需求,修改,调整一下代码即可...
FIRST.先说服务端的部分:
1.创建工程,如下图
2.代码结构
3.再说两个主要类,ZJJSocketService
.h 中 :
#import
#import "GCDAsyncSocket.h"
@interface ZJJSocketService : NSObject
//开启服务
- (void)start;
@end
#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
#import "ZJJSocketService.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
//创建服务对象
ZJJSocketService *socketSerview = [[ZJJSocketService alloc]init];
//开始服务
[socketSerview start];
//循环运行
[[NSRunLoop mainRunLoop]run];//目的让服务器不停止
}
return 0;
}
**************************************************************************************************
SECOND:再说客户端部分:
1.创建工程,结构如下图:
#import
#import "GCDAsyncSocket.h"
@interface GCDSocketManager : NSObject
@property(nonatomic,strong)GCDAsyncSocket *socket;
//单例
+ (instancetype)sharedSocketManager;
//连接
- (void)connectToServer;
//断开
- (void)cutOffSocket;
@end
#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:jsonData withTimeout:-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
**********************************************结束线*********************************************