SOCKET:TCP/IP网络的API。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。
一般有不定时的向服务器传送数据的需求时,使用长连接, iOS开发中长连接的使用一般使用第三方库AsyncSocket, 对socket的封装比较好,使用起来很方便.
下载AsyncSocket https://github.com/robbiehanson/CocoaAsyncSocket
将RunLoop文件夹下的AsyncSocket.h, AsyncSocket.m, AsyncUdpSocket.h, AsyncUdpSocket.m 文件拷贝到自己的project中
添加 依赖库 CFNetwork.framework
1. 先创建一个单例类,命名SocketTool
SocketTool.h
//
// SocketTool.h
// SocketTest
#import <Foundation/Foundation.h>
@interface SocketTool : NSObject
+ (SocketTool *)shareInstance;
@end
SocketTool.m
#import "SocketTool.h"
@implementation SocketTool
+ (SocketTool *)shareInstance {
static SocketTool *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end
2. 在SocketTool.h中导入
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>
#import "AsyncSocket.h"
声明变量
@property (nonatomic, strong) AsyncSocket *socket;
@property (nonatomic, copy) NSString *socketHost;
@property (nonatomic, assign) UInt16 socketPort; //端口号
@property (nonatomic, strong) NSTimer *connectTimer; // 计时器
声明连接和断开的方法
- (void)socketConnectHost; // socket连接
// 声明断开连接方法
- (void)cutOffSocket; // 断开socket连接.
代理
@interface SocketTool : NSObject<AsyncSocketDelegate>
枚举
enum {
SocketOfflineByServer, // 服务器掉线, 默认为0.
SocketOfflineByUser, //用户主动cut.
};
在SocketTool.m实现方法
//连接.
- (void)socketConnectHost {
self.socket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *error = nil;
[self.socket connectToHost:self.socketHost onPort:self.socketPort withTimeout:30 error:&error];
}
//切断socket
- (void)cutOffSocket {
self.socket.userData = SocketOfflineByUser; // 声明是由用户主动切断.
[self.connectTimer invalidate];
self.connectTimer = nil;
[self.socket disconnect];
}
在SocketTool.m成功 和 失败的回调方法
#pragma mark ----------------------- 连接成功回调 --------------------------
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
NSLog(@"连接成功");
// 每隔30秒向服务器发送心跳包 一般设置30秒发送一次心跳包
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
[self.connectTimer fire];
}
//--------------------------------- will连接失败回调 -------------------------------
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err {
NSLog(@"连接失败");
}
//--------------------------------- 连接失败回调 -------------------------------
// 重连
- (void)onSocketDidDisconnect:(AsyncSocket *)sock {
NSLog(@"sorry the connect is failure %ld", sock.userData);
if (sock.userData == SocketOfflineByServer) {
//服务器掉线, 重连
NSLog(@"服务器掉线**********请重新连接");
[self socketConnectHost];
} else if (sock.userData == SocketOfflineByUser) {
// 如果用户断开, 不进行重连.
return;
}
}
得到服务器下发消息会调用的方法
// 如果得到数据, 就会调用回调方法.
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(@"长连接下发的消息*****************%@",data);
// 对得到的data值进行解析与转换即可.
}
心跳连接方法
// 心跳连接
- (void)longConnectToSocket {
// 根据服务器要求发送固定格式的数据, 假设为指令@"longConnect", 但是一般不会是这么简单地指令.
NSLog(@"我发送了一次心跳包");
NSString *longConnect = @"longConnect";
NSData *dataStream = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:dataStream withTimeout:1 tag:1];
}
至此连接类已经完成
使用
导入#import "SocketTool.h"
[SocketTool shareInstance].socketHost = @"<code class="hljs actionscript"><span class="hljs-string"><span class="hljs-string">192.186.100.21</span></span></code>";// host设定
[SocketTool shareInstance].socketPort = 8282;// port设定
// 在连接前先进行手动断开
[SocketTool shareInstance].socket.userData = SocketOfflineByUser;
[[SocketTool shareInstance] cutOffSocket];
// 确保断开后再连,如果对一个正处于连接状态的socket进行连接,会出现崩溃
[SocketTool shareInstance].socket.userData = SocketOfflineByServer;
[[SocketTool shareInstance] socketConnectHost];