Socket简析与iOS实现
Socket的基本概念
1.定义
网络上两个程序通过一个双向通信连接实现数据交互,这种双向通信的连接叫做Socket(套接字)。
2.本质
网络模型中应用层与TCP/IP协议族通信的中间软件抽象层,是它的一组编程接口(API),也即对TCP/IP的封装。TCP/IP也要提供可供程序员做网络开发所用的接口,即Socket编程接口。
Socket的位置3.要素
Socket是网络通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信的必须的五种信息:
- 连接使用的协议
- 本地主机的IP地址
- 本地进程的协议端口
- 远程主机的IP地址
- 远程进程的协议端口
4.特性
Socket可以支持不同的传输协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接;同理,当使用UDP协议进行连接时,该Socket连接就是一个UDP连接。
多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
5.连接
建立Socket连接至少需要一对套接字,分别运行于服务端(ServerSocket)和客户端(ClientSocket)。套接字直接的连接过程氛围三个步骤:
Step 1 服务器监听
服务端Socket始终处于等待连接状态,实时监听是否有客户端请求连接。
Step 2 客户端请求
客户端Socket提出连接请求,指定服务端Socket的地址和端口号,这时就可以向对应的服务端提出Socket连接请求。
Step 3 连接确认
当服务端Socket监听到客户端Socket提出的连接请求时作出响应,建立一个新的进程,把服务端Socket的描述发送给客户端,该描述得到客户端确认后就可建立起Socket连接。而服务端Socket则继续处于监听状态,继续接收其他客户端Socket的请求。
iOS客户端Socket的实现
1. 数据流方式
- (IBAction)connectToServer:(id)sender {
// 1.与服务器通过三次握手建立连接
NSString *host = @"192.168.1.58";
int port = 1212;
//创建一个socket对象
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self
delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
NSError *error = nil;
// 开始连接
[_socket connectToHost:host
onPort:port
error:&error];
if (error) {
NSLog(@"%@",error);
}
}
#pragma mark - Socket代理方法
// 连接成功
- (void)socket:(GCDAsyncSocket *)sock
didConnectToHost:(NSString *)host
port:(uint16_t)port {
NSLog(@"%s",__func__);
}
// 断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock
withError:(NSError *)err {
if (err) {
NSLog(@"连接失败");
} else {
NSLog(@"正常断开");
}
}
// 发送数据
- (void)socket:(GCDAsyncSocket *)sock
didWriteDataWithTag:(long)tag {
NSLog(@"%s",__func__);
//发送完数据手动读取,-1不设置超时
[sock readDataWithTimeout:-1
tag:tag];
}
// 读取数据
-(void)socket:(GCDAsyncSocket *)sock
didReadData:(NSData *)data
withTag:(long)tag {
NSString *receiverStr = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(@"%s %@",__func__,receiverStr);
}
2.基于第三方开源库CocoaAsyncSocket
2.1客户端通过地址和端口号与服务端建立Socket连接,并写入相关数据。
- (void)connectToServerWithCommand:(NSString *)command
{
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
[_socket setUserData:command];
NSError *error = nil;
[_socket connectToHost:WIFI_DIRECT_HOST onPort:WIFI_DIRECT_PORT error:&error];
if (error) {
NSLog(@"__connect error:%@",error.userInfo);
}
[_socket writeData:[command dataUsingEncoding:NSUTF8StringEncoding] withTimeout:10.0f tag:6];
}
2.2 实现CocoaAsyncSocket的代理方法
#pragma mark - Socket Delegate
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
NSLog(@"Socket连接成功:%s",__func__);
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
if (err) {
NSLog(@"连接失败");
}else{
NSLog(@"正常断开");
}
if ([sock.userData isEqualToString:[NSString stringWithFormat:@"%d",SOCKET_CONNECT_SERVER]])
{
//服务器掉线 重新连接
[self connectToServerWithCommand:@"battery"];
}else
{
return;
}
}
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
NSLog(@"数据发送成功:%s",__func__);
//发送完数据手动读取,-1不设置超时
[sock readDataWithTimeout:-1 tag:tag];
}
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"读取数据:%s %@",__func__,receiverStr);
}
粘包与半包
关于粘包和半包,请看iOS 详解Socket编程粘包、半包处理。