socket连接

本文详细介绍了iOSSocket的工作原理和技术细节,包括Socket的概念、网络模型、网络协议(如HTTP、HTTPS、TCP、UDP)及其实现方法,还探讨了Socket在iOS中的具体应用,如BSDSocket、CFNetWork和NSStream等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

iOS Socket连接

1 socket简介和网络协议

Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换。

1.1 网络模型

OSI模型:应用层 表示层 会话层 传输层 网络层 数据链路层 物理层

eg:qq 应用层 表示层 会话层 发送文本信息hello

传输层 tcp/udp + hello

网络层 ip + tcp/udp + hello

数据链路层 mac地址 + llc + ip + tcp/udp + hello

物理层 比特流

简化模型:应用层 运输层 网络层 数据链路层 物理层

1.2 网络协议

  1. HTTP协议 超文本传输协议 一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统 基于TCP/IP 协议

2)HTTPS协议 安全套接字层超文本传输协议

HTTP和HTTPS区分:

1.https协议需要到ca申请证书,一般免费证书很少,需要交费。

2.http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。

3.http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4.http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

3)TCP 传输控制协议

特点

1、建立连接,形成传输通道

2、在连接中进行数据传输(数据不收大小控制)

3、通过三次握手完成连接,可靠的协议。保证消息完全到达

4、必须建立连接,所以效率很低

4)UDP 用户数据协议

特点

1、将数据内容和源地址、目的地址全部封装成一个数据包,不需要建立连接。

2、每个数据包有大小限制,限制在64k之内。

3、无需要连接,因此是不可靠的传输。

4、速度快

TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

1.3 TCP连接 Socket连接

TCP连接:

TCP的三次握手:

SYN(synchronous),同步标志,ACK (Acknowledgement),即确认标志,seq应该是Sequence Number,序列号的意思,另外还有四次握手的fin,应该是final,表示结束标志。

第一次握手:客户端发送一个TCP的SYN标志位置1的包指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。

第二次握手:服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的序列号加1以,即X+1。

第三次握手:客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1。并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写序列号的+1。

1)TCP连接和Socket连接的区分

socket层只是在TCP/UDP传输层上做的一个抽象接口层,因此一个socket连接可以基于连接,也有可能基于UDP。基于TCP协议的socket连接同样需要通过三次握手建立连接,是可靠的;基于UDP协议的socket连接不需要建立连接的过程,不过对方能不能收到都会发送过去,是不可靠的,大多数的即时通讯IM都是后者

2 基于TCP协议的 Socket连接

2.1 socket 通信

2.1.1 iOS网络编程层次:

Cocoa层:NSURL,NSStream,GameKit

Core Foundtion:基于C的CFNetWork 和 CFNetServices 其中CFNetWork 基于CFStream和CFSocket

OS层:基于C的BSD socket

2.1.2 socket 简介

Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换。

2.2 BSD Socket

API函数:

创建套接字

Socket(af,type,protocol)
复制代码

建立地址和套接字的联系

bind(sockid, local addr, addrlen)
复制代码

服务器端侦听客户端的请求

listen( Sockid ,quenlen)
复制代码

建立服务器/客户端的连接 (面向连接TCP)

客户端请求连接 
Connect(sockid, destaddr, addrlen)

服务器端等待从编号为Sockid的Socket上接收客户连接请求 
newsockid=accept(Sockid,Clientaddr, paddrlen)
复制代码

发送/接收数据

面向连接:
send(sockid, buff, bufflen) 
recv( )
面向无连接:
sendto(sockid,buff,…,addrlen) 
recvfrom( )
复制代码

释放套接字

close(sockid)
复制代码

TCP协议下的 Socket C/S 连接步骤

2.2.1 服务器工作流程

服务器调用 socket(...) 创建socket;

服务器blind(...) 将 socket 与特定主机地址与端口号绑定,成功绑定返回0,失败返回 -1。

服务器调用 listen(...) 设置缓冲区;

服务器通过 accept(...)接受客户端请求建立连接;

服务器与客户端建立连接之后,就可以通过 send(...)/receive(...)向客户端发送或从客户端接收数据;

服务器调用 close 关闭 socket;

2.2.2 客户端工作流程

客户端调用 socket(...) 创建socket;

客户端调用 connect(...) 向服务器发起连接请求以建立连接;

客户端与服务器建立连接之后,就可以通过 send(...)/receive(...)向客户端发送或从客户端接收数据;

客户端调用 close 关闭 socket;

1 )demo C 客户端 使用BSD socket API 环境 IPV4 服务端 nc -lk 端口号 始终监听本地计算机此端口的数据

2.3 CFNetWork

对BSD socket 轻量级封装 结合run-loop

demo 处理 API

CFNetwork API

CFNetwork 接口是基于 C 的,下面的接口用于创建一对 socket stream,一个用于读取,一个用于写入:

void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
复制代码

该函数使用 host 以及 port,CFNetwork 会将该 host 转换为 IP 地址,并转换为网络字节顺序。如果我们只需要一个 socket stream,我们可以将另外一个设置为 NULL。还有另外两个“重载”的创建 socket sream的接口:CFStreamCreatePairWithSocket 和 CFStreamCreatePairWithPeerSocketSignature。

在使用这些 socket stream 之前,必须显式地调用其 open 函数:

Boolean CFReadStreamOpen(CFReadStreamRef stream);

Boolean CFWriteStreamOpen(CFWriteStreamRef stream);
复制代码

但与 socket 不同的是,这两个接口是异步的,当成功 open 之后,如果调用方设置了获取 kCFStreamEventOpenCompleted 事件的标志的话就会其调用回调函数。

而该回调函数及其参数设置是通过如下接口进行的:

Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext);

Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
复制代码

该函数用于设置回调函数及相关参数。通过 streamEvents 标志来设置我们对哪些事件感兴趣;clientCB 是一个回调函数,当事件标志对应的事件发生时,该回调函数就会被调用;clientContext 是用于传递参数到回调函数中去。

当设置好回调函数之后,我们可以将 socket stream 当做事件源调度到 run-loop 中去,这样 run-loop 就能分发该 socket stream 的网络事件了。

void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);

void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
复制代码

将 socket stream 从 run-loop 的事件源中移除。

void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);

void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
复制代码

当我们将 socket stream 的网络事件调度到 run-loop 之后,我们就能在回调函数中相应各种事件,比如 kCFStreamEventHasBytesAvailable 读取数据:

Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream);

CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength);
复制代码

或 kCFStreamEventCanAcceptBytes 写入数据:

Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream);

CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength);
复制代码

最后,我们调用 close 方法关闭 socket stream:

void CFReadStreamClose(CFReadStreamRef stream);

void CFWriteStreamClose(CFWriteStreamRef stream);
复制代码

2.4 NSStream

1 使用ayscsocket搭建本地服务器

借助第三方Aysncsocket搭建本地服务器 demo。

终端 telnet IP地址 端口号

涉及的代理方法:

1)-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket

2)-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

3)-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag

2 基于本地的socket连接 使用NSStream

NSStream 实体类 NSInputStream NSOutputStream

NSStream API 接口

  • (void)open;
  • (void)close;
  • (id )delegate;
  • (void)setDelegate:(id )delegate;
  • (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
  • (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
  • (NSStreamStatus)streamStatus;
  • (NSError *)streamError;

NSInputStream 类有如下接口:

  • (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len; 从流中读取数据到 buffer 中,buffer 的长度不应少于 len,该接口返回实际读取的数据长度(该长度最大为 len)。

  • (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len; 获取当前流中的数据以及大小,注意 buffer 只在下一个流操作之前有效。

  • (BOOL)hasBytesAvailable; 检查流中是否还有数据。

NSOutputStream 类有如下接口:

  • (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)len; 将 buffer 中的数据写入流中,返回实际写入的字节数。

  • (BOOL)hasSpaceAvailable; 检查流中是否还有可供写入的空间。

demo:

1)iOS NSStream不支持 NShost

https://developer.apple.com/library/ios/qa/qa1652/_index.html

NSStream类不支持连接到远程主机,使用CFStream支持。两者可以通过toll-free桥接来相互转换。使用CFStream时,我们可以调用CFStreamCreatePairWithSocketToHost函数并传递主机名和端口号,来获取一个CFReadStreamRef和一个CFWriteStreamRef来进行通信,然后我们可以将它们转换为NSInputStream和NSOutputStream对象来处理。

2)使用NSStreamDelegate 处理流事件

-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值