初始化
//页面
%hook xxxxxxxViewController
//- (void)viewWillAppear:(BOOL)animated{
//NSLog(@"View Will Appear,再次进入刷新");
- (void)viewDidLoad{
//启动tcp
[[Xddtcp sharedTcpManager] connectServer] ;
}
发送数据
//发送数据
[[Xddtcp sharedTcpManager] sendDataToServer:[@"MyiPhone" dataUsingEncoding:NSUTF8StringEncoding]] ;
源码类
//
// Xddtcp.m
// YFX_ScoketForClientDemo
//
// Created by adminxdd on 2020/8/20.
// Copyright © 2020 fangxue. All rights reserved.
//
#import "Xddtcp.h"
#define WSELF __weak typeof(self) wself = self;
/** 主线程异步队列 */
#define Dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
//@implementation Xddtcp
@interface Xddtcp()
/** 心跳计时器 */
@property (nonatomic, strong) NSTimer *heartBeatTimer;
/** 没有网络的时候检测网络定时器 */
@property(nonatomic,strong)NSTimer*netWorkTestingTimer;
/** 存储要发送给服务端的数据 */
@property (nonatomic, strong) NSMutableArray *sendDataArray;
/** 数据请求队列(串行队列) */
@property (nonatomic, strong) dispatch_queue_t queue;
/** 重连时间 */
@property (nonatomic, assign) NSTimeInterval reConnectTime;
/** 用于判断是否主动关闭长连接,如果是主动断开连接,连接失败的代理中,就不用执行 重新连接方法 */
@property (nonatomic, assign) BOOL isActivelyClose;
/** 缓存区域 */
@property (nonatomic, strong) NSMutableData *readBuf;
@end
@implementation Xddtcp
#pragma mark -
#pragma mark ----单利------
/** 单利 */
+ (instancetype)sharedTcpManager{
static Xddtcp*_instace =nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
_instace = [[self alloc]init];
NSLog(@"TCP 单利实例化!");
});
return _instace;
}
#pragma mark ----初始化------
/** 初始化 */
- (instancetype)init{
self= [super init];
if(self){
self.reConnectTime = 0;
self.isActivelyClose = NO;
self.queue = dispatch_queue_create("BF",NULL);
self.sendDataArray = [[NSMutableArray alloc] init];
// self.mRecvPacket = [JHreceivePacket sharedManager];
// self.deviceListArr = [[NSMutableArray alloc] init];
// DLogInfo(@"初始化!");
}
return self;
}
#pragma mark -
#pragma mark --- 心跳计时器处理 -----
/** 心跳计时器初始化 */
- (void)initHeartBeat{
//心跳没有被关闭
if(self.heartBeatTimer)
{
return;
}
[self destoryHeartBeat];
//连接成功 先发一次 心跳数据 在打开计时器
//[[JHSocketData sharedManager] heartbeat];
//判断是否打开
if([xddTools keyGet:@"isOpenTCP"]&&[[xddTools keyGet:@"isOpenTCP"] isEqualToString:@"已打开"]){
[self.asyncTcpSocket writeData:[@"MyiPhone" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
}
WSELF
Dispatch_main_async_safe(^{
wself.heartBeatTimer = [NSTimer timerWithTimeInterval:30 target:wself selector:@selector(senderheartBeat) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop]addTimer:wself.heartBeatTimer forMode:NSRunLoopCommonModes];
});
}
#pragma mark --- 取消心跳 -----
/** 取消心跳 */
- (void)destoryHeartBeat
{
WSELF
Dispatch_main_async_safe(^{
if(wself.heartBeatTimer){
[wself.heartBeatTimer invalidate];
wself.heartBeatTimer =nil;
}
});
}
#pragma mark --- 没有网络的时候开始定时 -- 用于网络检测 -----
/** 没有网络,进行网络监测 */
- (void)noNetWorkStartTestingTimer{
WSELF
Dispatch_main_async_safe(^{
wself.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:wself selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:wself.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
});
}
#pragma mark --- 取消网络监测 ----
/** 取消网络监测 */
- (void)destoryNetWorkStartTesting
{
WSELF
Dispatch_main_async_safe(^{
if(wself.netWorkTestingTimer){
[wself.netWorkTestingTimer invalidate];
wself.netWorkTestingTimer =nil;
}
});
}
#pragma mark -
#pragma mark --- 发送心跳 ---
/** 发送心跳 */
- (void)senderheartBeat{
// 和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
[self.asyncTcpSocket writeData:[@"ok" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
// WSELF;
// Dispatch_main_async_safe(^{
//
// });
}
#pragma mark --- 定时检查网络 ---
/** 定时检查网络 */
- (void)noNetWorkStartTesting{
//有网络
if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
{
//关闭网络检测定时器
[self destoryNetWorkStartTesting];
//开始重连
[self reConnectServer];
}
}
#pragma mark -
#pragma mark ----建立连接------
/** 建立连接 */
-(void)connectServer{
self.isActivelyClose = NO;
NSError*error =nil;
_asyncTcpSocket= [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSString* ip = [xddTools keyGet:@"TCP_IP"];
NSString* port_str = [xddTools keyGet:@"TCP_Port"];
if([ip isEqualToString:@""]){
ip = @"192.168.1.2";
[xddTools keySet:@"TCP_IP" v:ip];
}
if([port_str isEqualToString:@""]){
port_str = @"12485";
[xddTools keySet:@"TCP_Port" v:port_str];
}
UInt16 port = [port_str integerValue];
//你的ip地址和端口号
[_asyncTcpSocket connectToHost:ip onPort:port withTimeout:60 error:&error];
if(error) {
NSLog(@"TCP连接错误:%@", error);
}
[self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
}
#pragma mark --- 重连服务器 ---
/** 重连服务器 */
- (void)reConnectServer{
if (self.asyncTcpSocket.isConnected) {
return;
}
if (self.reConnectTime > 10240) {
self.reConnectTime = 0;
return;
}
WSELF;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reConnectTime *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if(wself.asyncTcpSocket.isConnected)
{
return;
}
[wself connectServer];
NSLog(@"正在重连%f",wself.reConnectTime);
if(wself.reConnectTime==0){ //重连时间2的指数级增长
wself.reConnectTime=2;
} else {
wself.reConnectTime*=2;
}
});
NSLog(@"重连服务器!");
}
#pragma mark ----关闭连接------
/** 关闭连接 */
- (void)disConnectServer {
NSLog(@"dealloc");
// 关闭套接字
self.isActivelyClose = YES;
[self.asyncTcpSocket disconnect];
//关闭心跳定时器
[self destoryHeartBeat];
//关闭网络检测定时器
[self destoryNetWorkStartTesting];
self.asyncTcpSocket = nil;
}
#pragma mark ---代理 连接成功 ---
//连接成功
-(void)socket:(GCDAsyncSocket*)sock didConnectToHost:(NSString*)host port:(uint16_t)port
{
NSLog(@"TCP连接成功!");
// [self.asyncTcpSocket readDataWithTimeout:-1 tag:index];
// [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
// 存储接收数据的缓存区,处理数据的粘包和断包
self.readBuf = [[NSMutableData alloc]init];
[self initHeartBeat]; //开启心跳
//如果有尚未发送的数据,继续向服务端发送数据
if([self.sendDataArray count] >0){
[self sendeDataToServer];
}
}
#pragma mark ---代理 连接失败 ---
//连接失败 进行重连操作
-(void)socketDidDisconnect:(GCDAsyncSocket*)sock withError:(NSError*)err{
//用户主动断开连接,就不去进行重连
if(self.isActivelyClose)
{
return;
}
[self destoryHeartBeat]; //断开连接时销毁心跳
//判断网络环境
if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) //没有网络
{
[self noNetWorkStartTestingTimer];//开启网络检测定时器
}
else//有网络
{
[self reConnectServer];//连接失败就重连
}
NSLog(@"TCP连接失败!");
}
#pragma mark --- 消息发送成功 ---
-(void)socket:(GCDAsyncSocket*)sock didWriteDataWithTag:(long)tag{
NSLog(@"TCP发送成功");
}
#pragma mark -
#pragma mark --- 发送数据 ---
-(void)sendDataToServer:(id)data{
[self.sendDataArray addObject:data];
[self sendeDataToServer];
// [self.asyncTcpSocket writeData:data withTimeout:60 tag:0];
}
#pragma mark --- 发送数据给服务器 详细处理 ---
/** 发送数据的详细处理 */
-(void)sendeDataToServer{
WSELF
//把数据放到一个请求队列中
dispatch_async(self.queue, ^{
//网络判断 没有网络的情况
if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
//开启网络检测定时器
[wself noNetWorkStartTestingTimer];
}else{ //有网络情况
//判断对象是否存在
if(wself.asyncTcpSocket != nil) {
//判断TCP是否处于连接状态
if(wself.asyncTcpSocket.isConnected) {
//判断数据 是否存在
if(wself.sendDataArray.count>0) {
id sendData = wself.sendDataArray[0];
NSLog(@"TCP发送出去的数据: %@",sendData);
[self.asyncTcpSocket writeData:sendData withTimeout:30 tag:0];
[wself.sendDataArray removeObjectAtIndex:0];
if([wself.sendDataArray count] >0){
[wself sendeDataToServer];
}
}
}else{
//TCP 处于断开状态
//主动去重连服务器 连接成功后 继续发送数据
[wself reConnectServer];
}
}else{
[wself reConnectServer];
}
}
});
}
#pragma mark -
#pragma mark --- 接收的数据 ---
-(void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag
{
// sleep(1);
//此方法处理数据的黏包或者断包
NSLog(@"原始数据%@",data);
NSString*msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"xdd接收的数据 = %@",msg);
//断包处理
[self didReadData:data];
[self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
NSDictionary*retDic = [xddTools dictionaryWithJsonString:msg];
if(retDic == nil || ! [retDic isKindOfClass:[NSDictionary class]]) {
return ;
}
NSString*type_ = retDic[@"type"];
NSString*data_ = retDic[@"data"];
NSString*msgid_ = retDic[@"msgid"]?retDic[@"msgid"]:@"test";
NSLog(@"xdd接收的数据type_=%@,type_=%@",type_,data_);
//开始主线程
// dispatch_async(dispatch_get_main_queue(), ^{
//开始子线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
if(type_ && data_ && [type_ isEqualToString:@"1"]){
//获取森林信息
NSDictionary*dic = [xddTools getMYinfo:data_];
BOOL success = [dic[@"success"] boolValue]; //是否成功
NSString*resultDesc = dic[@"resultDesc"];//信息提示
NSString*resultCode = dic[@"resultCode"];//返回代码
NSString*currentEnergy = dic[@"treeEnergy"][@"currentEnergy"];//当前能量
NSDictionary*userEnergy = dic[@"userEnergy"];
NSString*loginId = userEnergy[@"loginId"];//[email protected]
NSString*userId = userEnergy[@"userId"];//2088*****
NSString*headPortrait = userEnergy[@"headPortrait"];//头像
NSString*displayName = userEnergy[@"displayName"];//昵称 茶
NSString*energySummation = userEnergy[@"energySummation"];//合计能量
NSString*treeAmount = userEnergy[@"treeAmount"];//证书个数
NSDictionary *dicRetInfo = [NSDictionary dictionaryWithObjectsAndKeys:
msgid_,@"msgid"
,type_,@"type"
,resultCode,@"code"
,currentEnergy,@"current"
,energySummation,@"summation"
,userId,@"userId"
,loginId,@"loginId"
,headPortrait,@"png"
,treeAmount,@"tree"
,displayName,@"name"
,nil];
NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dicRetInfo] dataUsingEncoding:NSUTF8StringEncoding];
//NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dicRetInfo options:NSJSONWritingPrettyPrinted error:nil];
[self sendDataToServer:jsonData];
}else if(type_ && data_ && ( [type_ isEqualToString:@"2"] || [type_ isEqualToString:@"4"])){
NSDictionary*dic;
if([type_ isEqualToString:@"2"]){
// 查询用户手机 昵称信息
dic = [xddTools getiPhoneInfo:data_];
}else if([type_ isEqualToString:@"4"]){
// 查询用户ID昵称信息
dic = [xddTools getuseriDInfo:data_];
}
NSString*account = dic[@"userAccount"];
NSString*userID = dic[@"userID"];
NSString*nickl = dic[@"userNicklName"];
NSString*name = dic[@"userName"];
NSString*status = dic[@"resultStatus"];
NSString*Suffix = dic[@"userNameSuffix"];
NSString*RealName = dic[@"userRealName"];
NSString*gender = dic[@"gender"];
//userNameSuffix 实名名字
//userRealName 注册名字
//gender 性别
NSDictionary *dicRetInfo = [NSDictionary dictionaryWithObjectsAndKeys:
msgid_,@"msgid"
,type_,@"type"
,status,@"status"
,account,@"account"
,userID,@"userID"
,nickl,@"nickl"
,name,@"name"
,Suffix,@"Suffix"
,RealName,@"RealName"
,gender,@"gender"
,nil];
//625 对方账户存在异常,不能进行当前操作 100 ok
NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dicRetInfo] dataUsingEncoding:NSUTF8StringEncoding];
//NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dicRetInfo options:NSJSONWritingPrettyPrinted error:nil];
[self sendDataToServer:jsonData];
}else if(type_ && data_ && [type_ isEqualToString:@"3"]){
//获取合种二维码信息
NSDictionary*dic = [xddTools gethezhongqrCode:data_];
// BOOL success = [dic[@"success"] boolValue]; //是否成功
// NSString*resultDesc = dic[@"resultDesc"];//信息提示
// NSString*resultCode = dic[@"resultCode"];//返回代码
// 可变字典 转可变字典
NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
[dict002 setObject:msgid_ forKey:@"msgid"];
[dict002 setObject:type_ forKey:@"type"];
[dict002 removeObjectForKey:@"extInfo"];
// NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dict002 options:NSJSONWritingPrettyPrinted error:nil];
[self sendDataToServer:jsonData];
}
else if(type_ && data_ && [type_ isEqualToString:@"5"]){
//获取签名
NSDictionary*dic = [xddTools getAlipaysign:retDic];
NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
[dict002 setObject:msgid_ forKey:@"msgid"];
[dict002 setObject:type_ forKey:@"type"];
NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
[self sendDataToServer:jsonData];
}
else if(type_ && data_ && [type_ isEqualToString:@"6"]){
//获取签名
NSDictionary*dic = [xddTools getAlipaysignV1:retDic];
NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
[dict002 setObject:msgid_ forKey:@"msgid"];
[dict002 setObject:type_ forKey:@"type"];
NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
[self sendDataToServer:jsonData];
}
else if(type_ && data_ && [type_ isEqualToString:@"xxx"]){
}
});
}
#pragma mark -
#pragma mark --- 黏包 断包处理 ---
-(void) didReadData:(NSData*)data {
//断包处理 要根据 你的 数据的 长度标识位的数据 来判断 读到什么地方 才是你完整的数据。根据协议去走
_readBuf = [NSMutableData dataWithData:data];
// 取出4-8位保存的数据长度,计算数据包长度
while(_readBuf.length>=5) {// 头数据为5个字节
// 得到数据的ID 和 整个数据的长度
NSData *dataLength = [_readBuf subdataWithRange:NSMakeRange(3, 2)];
Byte*ByteLength = (Byte*)[dataLength bytes];
int headLen = (ByteLength[0] &0x00ff) + ((ByteLength[1] &0x00ff) <<8);
NSInteger lengthInteger =0;
lengthInteger = (NSInteger)headLen;
NSInteger complateDataLength = lengthInteger +6;//算出一个包完整的长度(内容长度+头长度)
NSLog(@"已读取数据:缓冲区长度:%ld, 接收长度:%lu 数据长度:%ld ", (long)_readBuf.length, (unsigned long)[data length], (long)complateDataLength);
// NSInteger dataLength = length + 2;
if(_readBuf.length>= complateDataLength) {//如果缓存中的数据 够 一个整包的长度
NSData*msgData = [_readBuf subdataWithRange:NSMakeRange(0, complateDataLength)];
// 处理消息数据
NSLog(@"得到完整包数据:%@",msgData);
// 从缓存中截掉处理完的数据,继续循环
_readBuf= [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(complateDataLength,_readBuf.length- complateDataLength)]];
// [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
}else{// 断包情况,继续读取
// [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
// [sock readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//继续读取数据
[self.asyncTcpSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];
return;
}
[self.asyncTcpSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//继续读取数据
}
}
@end
//
// Xddtcp.h
// YFX_ScoketForClientDemo
//
// Created by adminxdd on 2020/8/20.
// Copyright © 2020 fangxue. All rights reserved.
//
#import
#import "AFNetworkReachabilityManager.h"
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
#import
#import "GCDAsyncSocket.h"
#import "xddTools.h"
//@interface SingleTcpCase :NSObject
@interface Xddtcp : NSObject
@property(strong,nonatomic)GCDAsyncSocket *asyncTcpSocket;
/** 单利初始化 */
+(instancetype)sharedTcpManager;
/** 建立连接 */
-(void)connectServer;
/** 关闭连接 */
-(void)disConnectServer;
/** 发送数据给服务器 */
-(void)sendDataToServer:(id)data;
@end
配置端口
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请输入配置信息" message:@"++++++" preferredStyle:UIAlertControllerStyleAlert];
//增加取消按钮;
[alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
//增加确定按钮;
[alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
//获取第1个输入框;
UITextField *userNameTextField = alertController.textFields.firstObject;
//获取第2个输入框;
UITextField *passwordTextField = alertController.textFields.lastObject;
//NSLog(@"用户名 = %@,密码 = %@",userNameTextField.text,passwordTextField.text);
if(userNameTextField.text && ![userNameTextField.text isEqualToString:@""]){
[xddTools keySet:@"TCP_IP" v:userNameTextField.text];
}
if(passwordTextField.text && ![passwordTextField.text isEqualToString:@""]){
[xddTools keySet:@"TCP_Port" v:passwordTextField.text];
}
}]];
//定义第一个输入框;
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"请输入配置IP";
textField.text = [xddTools keyGet:@"TCP_IP"];
}];
//定义第二个输入框;
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"请输入配置端口";
textField.text = [xddTools keyGet:@"TCP_Port"];
}];
[weakSelf presentViewController:alertController animated:true completion:nil];
+(NSString*)keyGet:(NSString*)k
{
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
return [userDefault objectForKey:k]?[userDefault objectForKey:k]:@"";
// - objectForKey:
// - URLForKey:
// - arrayForKey:
// - dictionaryForKey:
// - stringForKey:
// - stringArrayForKey:
// - dataForKey:
// - boolForKey:
// - integerForKey:
// - floatForKey:
// - doubleForKey:
// - dictionaryRepresentation
}
+(void)keySet:(NSString*)k v:(NSString*)v
{
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:v forKey:k];
[userDefault synchronize];
// - setObject:forKey:
// - setFloat:forKey:
// - setDouble:forKey:
// - setInteger:forKey:
// - setBool:forKey:
// - setURL:forKey:
}