用socket可以实现像QQ那样发送即时消息的功能。客户端和服务端需要建立长连接,在长连接的情况下,发送消息。客户端可以发送心跳包来检测长连接。
在iOS开发中使用socket,一般都是用第三方库AsyncSocket,不得不承认这个库确实很强大。下载地址CocoaAsyncSocket
。
特性
AsyncSocket类是支持TCP的。
AsyncUdpSocket是支持UDP的。
AsyncSocket是封装了CFSocket和CFSteam的TCP/IP socket网络库。它提供了异步操作,本地cocoa类的基于delegate的完整支持。主要有以下特性:
AsyncUdpSocket是UDP/IP socket网络库,包装自CFSocket。它的工作很像TCP版本,只不过是用于处理UDP的。它包括基于非阻塞队列的发送接收操作,完整的委托支持,基于runloop,自包含的类,以及支持IPV4和IPV6。
使用AsyncSocket的时候可以做一层封装,根据需求提供几个接口出来。比如:连接、断开连接、发送消息等等。还有接受消息,接受到的消息可以通过通知、代理、block等传出去。
下面简单介绍一下对AsyncSocket使用.一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。
定义单列类:LGSocketServe
LGSocketServe.h
#import
#import "AsyncSocket.h"
@interface LGSocketServe : NSObject
+ (LGSocketServe *)sharedSocketServe;
@end
#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
LGSocketServe.h
// socket连接
- (void)startConnectSocket;
LGSocketServe.m
//自己设定
#define HOST @"192.168.0.1"
#define PORT 8080
//设置连接超时
#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;
}
LGSocketServe.h
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//这是异步返回的连接成功,
NSLog(@"didConnectToHost");
//通过定时器不断发送消息,来检测长连接
self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];
[self.heartTimer fire];
}
// 心跳连接
-(void)checkLongConnectByServe{
// 向服务器发送固定可是的消息,来检测长连接
NSString *longConnect = @"connect is here";
NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:1 tag:1];
}
断开连接
1,用户手动断开连接
LGSocketServe.h
-(void)cutOffSocket
{
self.socket.userData = SocketOfflineByUser;
[self.socket disconnect];
}
2,wifi断开,socket断开连接
LGSocketServe.m
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
重新连接
socket断开之后会回调:
LGSocketServe.m
NSLog(@"7878 sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服务器掉线,重连
[self startConnectSocket];
}
else if (sock.userData == SocketOfflineByUser) {
// 如果由用户断开,不进行重连
return;
}else if (sock.userData == SocketOfflineByWifiCut) {
// wifi断开,不进行重连
return;
}
}
发送消息
LGSocketServe.h
//设置写入超时 -1 表示不会使用超时
#define WRITE_TIME_OUT -1
- (void)sendMessage:(id)message
{
//像服务器发送数据
NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];
}
//发送消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
}
接受消息
LGSocketServe.m
#define MAX_BUFFER 1024
//发送消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
//读取消息
[self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
}
//接受消息成功之后回调
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
//服务端返回消息数据量比较大时,可能分多次返回。所以在读取消息的时候,设置MAX_BUFFER表示每次最多读取多少,当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析
if( data.length < MAX_BUFFER )
{
//收到结果解析...
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dic);
//解析出来的消息,可以通过通知、代理、block等传出去
}
[self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
出错处理
LGSocketServe.m
NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]);
if (err.code == 57) {
self.socket.userData = SocketOfflineByWifiCut;
}
}
}
使用
导入#import “LGSocketServe.h”
//发送消息 @"hello world"只是举个列子,具体根据服务端的消息格式
[socketServe sendMessage:@"hello world"];