- 相關(guān)推薦
iOS應(yīng)用中使用AsyncSocket庫(kù)處理Socket通信的用法
用socket可以實(shí)現(xiàn)像QQ那樣發(fā)送即時(shí)消息的功能?蛻舳撕头⻊(wù)端需要建立長(zhǎng)連接,在長(zhǎng)連接的情況下,發(fā)送消息。客戶端可以發(fā)送心跳包來(lái)檢測(cè)長(zhǎng)連接。
在iOS開(kāi)發(fā)中使用socket,一般都是用第三方庫(kù)AsyncSocket,不得不承認(rèn)這個(gè)庫(kù)確實(shí)很強(qiáng)大。下載地址CocoaAsyncSocket。
特性
AsyncSocket類是支持TCP的。
AsyncUdpSocket是支持UDP的。
AsyncSocket是封裝了CFSocket和CFSteam的TCP/IP socket網(wǎng)絡(luò)庫(kù)。它提供了異步操作,本地cocoa類的基于delegate的完整支持。主要有以下特性:
隊(duì)列的非阻塞的讀和寫(xiě),而且可選超時(shí)。你可以調(diào)用它讀取和寫(xiě)入,它會(huì)當(dāng)完成后告知你。
自動(dòng)的socket接收。如果你調(diào)用它接收連接,它將為每個(gè)連接啟動(dòng)新的實(shí)例,當(dāng)然,也可以立即關(guān)閉這些連接。
委托(delegate)支持。錯(cuò)誤、連接、接收、完整的讀取、完整的寫(xiě)入、進(jìn)度以及斷開(kāi)連接,都可以通過(guò)委托模式調(diào)用。
基于run loop的,而不是線程的。雖然可以在主線程或者工作線程中使用它,但你不需要這樣做。它異步的調(diào)用委托方法,使用NSRunLoop。委托方法包括socket的參數(shù),可讓你在多個(gè)實(shí)例中區(qū)分。
自包含在一個(gè)類中。你無(wú)需操作流或者socket,這個(gè)類幫你做了全部。
支持基于IPV4和IPV6的TCP流。
AsyncUdpSocket是UDP/IP socket網(wǎng)絡(luò)庫(kù),包裝自CFSocket。它的工作很像TCP版本,只不過(guò)是用于處理UDP的。它包括基于非阻塞隊(duì)列的發(fā)送接收操作,完整的委托支持,基于runloop,自包含的類,以及支持IPV4和IPV6。
使用AsyncSocket的時(shí)候可以做一層封裝,根據(jù)需求提供幾個(gè)接口出來(lái)。比如:連接、斷開(kāi)連接、發(fā)送消息等等。還有接受消息,接受到的消息可以通過(guò)通知、代理、block等傳出去。
下面簡(jiǎn)單介紹一下對(duì)AsyncSocket使用.一般來(lái)說(shuō),一個(gè)用戶只需要建立一個(gè)socket長(zhǎng)連接,所以可以用單例類方便使用。
定義單列類:LGSocketServe
LGSocketServe.h
復(fù)制代碼 代碼如下:
//
// LGSocketServe.h
// AsyncSocketDemo
//
#import
#import "AsyncSocket.h"
@interface LGSocketServe : NSObject
+ (LGSocketServe *)sharedSocketServe;
@end
LGSocketServe.m
復(fù)制代碼 代碼如下:
//
// LGSocketServe.m
// AsyncSocketDemo
//
#import "LGSocketServe.h"
@implementation LGSocketServe
static LGSocketServe *socketServe = nil;
#pragma mark public static methods
+ (LGSocketServe *)sharedSocketServe {
@synchronized(self) {
if(socketServe == nil) {
socketServe = [[[self class] alloc] init];
}
}
return socketServe;
}
+(id)allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (socketServe == nil)
{
socketServe = [super allocWithZone:zone];
return socketServe;
}
}
return nil;
}
@end
建立socket長(zhǎng)連接
LGSocketServe.h
復(fù)制代碼 代碼如下:
@property (nonatomic, strong) AsyncSocket *socket; // socket
// socket連接
- (void)startConnectSocket;
LGSocketServe.m
//自己設(shè)定
#define HOST @"192.168.0.1"
#define PORT 8080
//設(shè)置連接超時(shí)
#define TIME_OUT 20
- (void)startConnectSocket
{
self.socket = [[AsyncSocket alloc] initWithDelegate:self];
[self.socket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
if ( ![self SocketOpen:HOST port:PORT] )
{
}
}
- (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port
{
if (![self.socket isConnected])
{
NSError *error = nil;
[self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error];
}
return 0;
}
宏定義一下HOST、PORT、TIME_OUT,實(shí)現(xiàn)startConnectSocket方法。這個(gè)時(shí)候要設(shè)置一下AsyncSocket的代理AsyncSocketDelegate。當(dāng)長(zhǎng)連接成功之后會(huì)調(diào)用:
復(fù)制代碼 代碼如下:
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//這是異步返回的連接成功,
NSLog(@"didConnectToHost");
}
心跳
LGSocketServe.h
復(fù)制代碼 代碼如下:
@property (nonatomic, retain) NSTimer *heartTimer; // 心跳計(jì)時(shí)器
LGSocketServe.m
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//這是異步返回的連接成功,
NSLog(@"didConnectToHost");
//通過(guò)定時(shí)器不斷發(fā)送消息,來(lái)檢測(cè)長(zhǎng)連接
self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];
[self.heartTimer fire];
}
// 心跳連接
-(void)checkLongConnectByServe{
// 向服務(wù)器發(fā)送固定可是的消息,來(lái)檢測(cè)長(zhǎng)連接
NSString *longConnect = @"connect is here";
NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:1 tag:1];
}
在連接成功的回調(diào)方法里,啟動(dòng)定時(shí)器,每隔2秒向服務(wù)器發(fā)送固定的消息來(lái)檢測(cè)長(zhǎng)連接。(這個(gè)根據(jù)服務(wù)器的需要就可以了)
斷開(kāi)連接
1,用戶手動(dòng)斷開(kāi)連接
LGSocketServe.h
復(fù)制代碼 代碼如下:
// 斷開(kāi)socket連接
-(void)cutOffSocket;
LGSocketServe.m
-(void)cutOffSocket
{
self.socket.userData = SocketOfflineByUser;
[self.socket disconnect];
}
cutOffSocket是用戶斷開(kāi)連接之后,不在嘗試重新連接。
2,wifi斷開(kāi),socket斷開(kāi)連接
LGSocketServe.m
復(fù)制代碼 代碼如下:
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
wifi斷開(kāi)之后,會(huì)回調(diào)onSocket:willDisconnectWithError:方法,err.code == 57,這個(gè)時(shí)候設(shè)置self.socket.userData = SocketOfflineByWifiCut。
重新連接
socket斷開(kāi)之后會(huì)回調(diào):
LGSocketServe.m
復(fù)制代碼 代碼如下:
- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSLog(@"7878 sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服務(wù)器掉線,重連
[self startConnectSocket];
}
else if (sock.userData == SocketOfflineByUser) {
// 如果由用戶斷開(kāi),不進(jìn)行重連
return;
}else if (sock.userData == SocketOfflineByWifiCut) {
// wifi斷開(kāi),不進(jìn)行重連
return;
}
}
在onSocketDidDisconnect回調(diào)方法里面,會(huì)根據(jù)self.socket.userData來(lái)判斷是否需要重新連接。
發(fā)送消息
LGSocketServe.h
復(fù)制代碼 代碼如下:
// 發(fā)送消息
- (void)sendMessage:(id)message;
LGSocketServe.m
//設(shè)置寫(xiě)入超時(shí) -1 表示不會(huì)使用超時(shí)
#define WRITE_TIME_OUT -1
- (void)sendMessage:(id)message
{
//像服務(wù)器發(fā)送數(shù)據(jù)
NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];
}
//發(fā)送消息成功之后回調(diào)
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
}
發(fā)送消息成功之后會(huì)調(diào)用onSocket:didWriteDataWithTag:,在這個(gè)方法里可以進(jìn)行讀取消息。
接受消息
LGSocketServe.m
復(fù)制代碼 代碼如下:
//設(shè)置讀取超時(shí) -1 表示不會(huì)使用超時(shí)
#define READ_TIME_OUT -1
#define MAX_BUFFER 1024
//發(fā)送消息成功之后回調(diào)
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
//讀取消息
[self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
}
//接受消息成功之后回調(diào)
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
//服務(wù)端返回消息數(shù)據(jù)量比較大時(shí),可能分多次返回。所以在讀取消息的時(shí)候,設(shè)置MAX_BUFFER表示每次最多讀取多少,當(dāng)data.length < MAX_BUFFER我們認(rèn)為有可能是接受完一個(gè)完整的消息,然后才解析
if( data.length < MAX_BUFFER )
{
//收到結(jié)果解析...
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dic);
//解析出來(lái)的消息,可以通過(guò)通知、代理、block等傳出去
}
[self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
接受消息后去解析,然后可以通過(guò)通知、代理、block等傳出去。在onSocket:didReadData:withTag:回調(diào)方法里面需要不斷讀取消息,因?yàn)閿?shù)據(jù)量比較大的話,服務(wù)器會(huì)分多次返回。所以我們需要定義一個(gè)MAX_BUFFER的宏,表示每次最多讀取多少。當(dāng)data.length < MAX_BUFFER我們認(rèn)為有可能是接受完一個(gè)完整的消息,然后才解析 。
出錯(cuò)處理
LGSocketServe.m
復(fù)制代碼 代碼如下:
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSData * unreadData = [sock unreadData]; // ** This gets the current buffer
if(unreadData.length > 0) {
[self onSocket:sock didReadData:unreadData withTag:0]; // ** Return as much data that could be collected
} else {
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
}
socket出錯(cuò)會(huì)回調(diào)onSocket:willDisconnectWithError:方法,可以通過(guò)unreadData來(lái)讀取未來(lái)得及讀取的buffer。
使用
導(dǎo)入#import “LGSocketServe.h”
復(fù)制代碼 代碼如下:
LGSocketServe *socketServe = [LGSocketServe sharedSocketServe];
//socket連接前先斷開(kāi)連接以免之前socket連接沒(méi)有斷開(kāi)導(dǎo)致閃退
[socketServe cutOffSocket];
socketServe.socket.userData = SocketOfflineByServer;
[socketServe startConnectSocket];
//發(fā)送消息 @"hello world"只是舉個(gè)列子,具體根據(jù)服務(wù)端的消息格式
[socketServe sendMessage:@"hello world"];
【iOS應(yīng)用中使用AsyncSocket庫(kù)處理Socket通信的用法】相關(guān)文章:
Windows7中使用Windows Live照片庫(kù)05-10
IOS7新功能Airdrop使用教程03-22
iOS11后臺(tái)應(yīng)用刷新怎么設(shè)置04-05
ios9.3如何卸載iphone預(yù)裝應(yīng)用05-05
ios7八門神器怎么使用06-04
小米手環(huán)數(shù)據(jù)導(dǎo)入iphone ios8健康應(yīng)用教程05-05
ios7.1.2怎么升級(jí)ios803-18
ios7如何降級(jí)到ios604-28