新进的这家公司搞智能家居,就随便整理一下其相关技术吧!首先,从GCDAsyncSocket的使用问题着手。
正如名称一样GCDAsyncSocket开源类库是以苹果的GCD多任务处理机制完成的一个异步交互套接字通讯。使用方法其实并不复杂。每一个GCDAsyncSocket对象(以下简称GCDSocket对象)都可以理解为一个socket套接字,我们的操作都是针对于这个socket执行的各种命令,可以打开一个端口侦听,同样也可以连接其他计算机的端口进行数据通讯等等等等。
首先我们来创建一个socket。源代码如下:
GCDAsyncSocket socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
代码并不复杂,我们只需要给出一个委托对象也就是第一个参数中的self,以及一个委托运行的GCD队列即可创建一个GCDAsyncSocket,当前代码中我们是使用静态全局函数取得的主消息队列。当然也可以使用其他方法获得其他的GCD队列,比如:dispatch_get_global_queue()。
创建了Socket对象我们即可以理解为,当前我们的socket已经进入程序以供操作。但如果你想和服务器进行通讯,那么我们还需要和服务器进行连接。可能有的使用习惯了http协议的人会问,初始化函数中我们为何不直接指定服务器以及端口号?其实这些肯定都是需要的,但是你要理解到,你的socket对象功能不只是可以用来连接服务器,换而言之我们的socket对象一样可以侦听某端口来等待他人连接,所以在通过套接字编程使用TCP协议的时候是我们从http协议过度到TCP协议的一个转变,我们要使用的是协议,并非某个类,所以我上述说明中都是说从http协议过度到TCP而不是跟大家说现在我们将从NSURLRequest和NSURLConnection过度到GCDAsyncSocket。
好了接下来我们看看如何连接服务器。源代码如下:
NSError *error = nil;
if (![socket connectToHost:@"192.168.16.254" onPort:5001 error:&error]) {
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:nil message:@"连接发生错误,无法配置!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alert show];
}else{
NSLog(@"连接成功");
}
我们只是先声明了一个错误信息的指针,然后使用之前创建的对象调用他的连接方法,它本身就是返回一个连接成功与否的bool值,使用很方便,第一个参数不难看出是一个IP,第二个参数则是一个端口,最后一个是出参,如果连接的过程中出现了错误,该方法会把这根指针指向一个具体的错误信息,最后我们再判断一下之前我们创建错误信息的指针是否还是指向空,如果并非指向空那么代表我们连接的过程中出现了错误,将错误信息打印一下吧~不过请切记,此处的错误信息并非你创建连接时所有的错误都会在此处得到反映。
说到这里我们该说一点真正有用的了,GCDAsyncSocket具有一系列完整的委托机制,我们所做的一切处理基本都是异步处理的状态,换句话说,连接之后是否连接成功,连接成功要执行什么懂并非应该写在此处而应该写在相应的委托之中,同样的道理一样适用于发送、读取数据等等。也就是说我们在此处读出的错误只是同步执行的代码处理一些连接时会发生的错误,而更多的处理我们应在相对应的委托中进行处理。首先请看下面这个方法:
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
这个方法就是在成功连接服务器之后的委托方法。在这个委托方法中,我们可以取到一个socket对象一个服务器IP和一个端口号,你可以处理一切在连接建立之后应该马上执行的事情,比如与服务器进行通信确认连接端以免出现其他人通过IP及端口随意的和你的服务器通信,再比如开启心跳包的发送,让服务器一直可以确认你的存在。不管做什么,都是你和服务器的编写者事前约定好的,就像数据传输格式什么的。
不管你要在此处都做什么工作,都要处理哪些事宜,请务必记得,在此处你必须要在函数的最后加上一句:
[socket readDataWithTimeout:-1 tag:0];
这是什么?按照你看到这个函数的第一反应取理解,没错他就是读取数据的方法,两个参数也略显简单,一个超时时间,如果你设置成-1则认为永不超时,而第二参数则是区别该次读取与其他读取的标志,通常我们在设计视图上的控件时也会有这样的一个属性就是tag。
现在我们的连接动作算是完整的做完了。接下来就是和服务器的交互处理了,也就是读写操作,通讯毕竟是为了传输数据的。
二、read
[asyncSocket readDataToLength:LENGTH_HEADER withTimeout:-1 tag:TAG_HEADER];
当然我们可以投递多个读写操作而不必等待上一个完成。
[asyncSocket readDataToLength:LENGTH_HEADER withTimeout:-1 tag:TAG_RESPONSE_BODY];
这样读请求会自动进入了队列,当socket 连接上后,请求会自动出队被执行。这个读操作完成后 socket:didReadData:withTag: 会被调用。
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag { if (tag == TAG_HEADER) { int bodyLength = [self parseHeader:data]; [socket readDataToLength:bodyLength withTimeout:-1 tag:TAG_RESPONSE_BODY]; } else if (tag == TAG_RESPONSE_BODY) { // Process the response [self handleResponseBody:data]; // Start reading the next response [socket readDataToLength:headerLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER]; } }
三、write (和read的用法一样。)
- (
void
)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(
long
)tag
{
if
(tag ==TAG_HEADER)
NSLog
(@
"TAG_HEADER request sent"
);
else
if
(tag ==TAG_BODY)
NSLog
(@
"TAG_BODY request sent"
);
}