iOS网络编程之Socket
1、套接字(socket)概念
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
简单来说,Socket更像是对传输层的简单封装,使的开发者可以更好的使用一些协议从而实现不用进程和应用之间的通讯。
以下是通讯示意图
通讯示意图这里关于TCP/UDP协议不过多介绍,TCP需要通过著名的三次握手进行连接,不懂的朋友可以看看这篇文章->计算机网络传输层知识点全覆盖
2、iOS Socket应用场景
Socket 使用长连接进行通讯,凡是一个来回网络请求解决不了问题的都会用socket。例如常见的IM类应用、音视频通话等。
与之对应的是HTTP连接,HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。例如我们下载图片、浏览微信朋友圈等。
3、常用的socket类型
常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;
数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
4、iOS使用的socket编程的方式有哪些?
第一种: BSD Socket
BSD Socket 是UNIX系统中通用的网络接口,它不仅支持各种不同的网络类型,而且也是一种内部进程之间的通信机制。而iOS系统其实本质就是UNIX,所以可以用,但是比较复杂。
第二种:CFSocket
CFSocket是苹果提供给我们的使用Socket的方式,但是用起来还是会不太顺手。当然想使用的话,可以细细研究一下。
第三种:CocoaAsyncSocket
CocoaAsyncSocket是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的、强大的异步套接字库,向上封装出简单易用OC接口。省去了我们面向Socket以及数据流Stream等繁琐复杂的编程。
5、使用CocoaAsyncSocket
首先点击上面的链接下载CocoaAsyncSocket的源码,把以下文件拷贝到自己的项目中:
CocoaAsyncSocket开发使用的文件
我们这里只是简单讲解GCDAsyncSocket的使用,它主要用于开发TCP的连接。由于TCP是长连接,我们需要在自己的项目中使GCDAsyncSocket保持全局唯一性,所以我们在项目里声明一个单例类 AsyncSocketManager以下是AsyncSocketManager.h的结构
@interface AsyncSocketManager : NSObject<GCDAsyncSocketDelegate>
+(AsyncSocketManager *)shareAsyncSocket;
@property (nonatomic,strong) GCDAsyncSocket *asyncSocket;
@property (nonatomic,copy) NSString *socketHost;//注意是ip地址,并不是DNS域名
@property (nonatomic,assign) UInt16 socketPort;
@property (nonatomic,strong) NSTimer *connectTimer;
-(void)socketConnectHost;//连接socket
-(void)cutoffSocket;//断开socket
@end
5.1 Socket的初始化连接
-(void)socketConnectHost{
//创建socket并指定代理对象为self,代理队列必须为主队列
self.asyncSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error;
[self.asyncSocket connectToHost:self.socketHost onPort:self.socketPort error:&error];
}
连接成功的代理方法
//连接成功的回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
[self addTimer];// 连接成功开启定时器
[self.asyncSocket readDataWithTimeout:- 1 tag:0];// 连接后,可读取服务端的数据
}
读取数据成功后的代理,连接成功之后需要开启一个定时器规定一定的时间后需要给服务器发送心跳包,用于检测是否服务器在线
// 添加定时器
- (void)addTimer
{
// 长连接定时器
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
// 把定时器添加到当前运行循环,并且调为通用模式
[[NSRunLoop currentRunLoop] addTimer:self.connectTimer forMode:NSRunLoopCommonModes];
}
/** 注意:
心跳连接中发送给服务端的数据只是作为测试代码,根据你们公司需求,
或者和后台商定好心跳包的数据以及发送心跳的时间间隔.
服务端也需要有对应的心跳检测,以此检测客户端是否在线.
*/
-(void)longConnectToSocket{
NSLog(@"我发送了一次心跳包");
NSString *longConnect = @"longConnect";//需要和服务器商量好
NSData *dataStream = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.asyncSocket writeData:dataStream withTimeout:1 tag:1];
}
/**
读取数据成功的回调
@param sock 客户端socket
@param data 读取到的数据
@param tag 本次读取的标记
*/
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"读取的数据==%@",text);
// 读取到服务端数据值后,能再次读取
[self.asyncSocket readDataWithTimeout:- 1 tag:0];//这里还需要进行读取操作,否则只能读取一次
}
5.2 断开连接
sokect断开连接时,需要清空代理和客户端本身的socket.
[self.asyncSocket disconnect];//断开连接
/**
客户端socket断开后的回调
@param sock 客户端socket
@param err 错误描述
*/
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
self.asyncSocket.delegate = nil;
self.asyncSocket = nil;
[self.connectTimer invalidate];
}
6、总结
CocoaAsyncSocket 是前人帮我们封装好的Socket,如果你想了解的更深的话也可以去了解Apple提供的BSD Socket、CFSocket等接口。
Socket都是用于应用之间进行与服务器之间需要长时间的连接,需要时刻的保持连接,已到达数据时效性。比较常用的就是IM类的应用,不过每一个IM类的应用企业都是自己封装的Socket以适应自己公司的不同业务功能需求。
虽然市面上有非常多的各种各样的Socket框架可以用,但我认为作为一名开发者,我们有责任去搞懂Socket通讯的原理,至少是需要了解。
好了,这篇文章就到这里啦~~~
我最近在逼着自己写文章,以前总是认为像这样的文章网上到处都是而且有一些大神写的非常好,so,我干嘛要写呢……不过最后想想还是认为有必要自己记录下来,别人的永远是别人的,只有自己消化了,理解了,那才能是你自己的东西!