iOS网络编程

作者: 陵无山 | 来源:发表于2018-08-01 21:41 被阅读119次

网络编程

1. 概论

    两个通信设备(具有网卡)通过网络进行数据的传播与交换
  • 建立连接:通过IP或者域名来连接两台设备,通过端口号找到对应的通信程序
  • 通信协议:要传输的数据需要根据约定的格式来创建,或者接受的数据需要按照约定的格式来还原
  • 数据传输方式:双工、长链接、可靠的,如TCP传输协议,单工、广播、不可靠的,如UDP传输协议
  • TCP/IP分层模式:将复杂的网络通信分拆,层的功能独立,下层为上层提供服务
  • TCP连接:三次握手

2. SOCKET原理

socket的概念

        socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。
        处于应用层和传输层之间;socket包含进行网络通信必须的五种信息:
                                ①连接使用的协议,
                                ②本地主机的IP地址,
                                ③本地进程的协议端口,
                                ④远地主机的IP地址,
                                ⑤远地进程的协议端口
        socket出现解决了“TCP同时为多个应用程序进程提供并发服务的问题”
  • socket的本质和实现:socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用==“打开open –> 读写write/read –> 关闭close”模式==来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

建立socket连接

        建立Socket连接至少需要一对套接字,其中一个运行于客户端,
        称为ClientSocket,另一个运行于服务器端,称为ServerSocket。
        套接字之间的连接过程分为三个步骤:
        1.服务器监听
        2.客户端请求
        3.连接确认

* 服务器监听
        
            服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,
            实时监控网络状态,等待客户端的连接请求。
            
* 使用socket()函数创建一个socket
        
                int socket(int domain, int type, int protocol);
    
    socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
    创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为:
            
    * **domain**:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
    * **type**:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
    * **protocol**:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议
      
* 调用bind()函数把一个地址族中的特定地址赋给socket
            
                int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合
    函数的三个参数分别为:    
    
    * **sockfd**:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
    * **addr**:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是: 
    * **addrlen**:对应的是地址的长度。
    
* 调用listen()函数将socket变为被动类型的,等待客户的连接请求

            int listen(int sockfd, int backlog);
        如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
  • 客户请求
    • 客户端这时调用connect()发出连接请求

        int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      
    connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
  • 连接确认
    • 服务端调用accept()函数取接收请求,这样连接就建立好了

        int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
      
    accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

    注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭

进行网络通信

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

服务器与客户已经建立好连接就可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()

断开连接

int close(int fd);

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

socket中TCP的三次握手建立连接

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

  • 客户端向服务器发送一个SYN J

  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1

  • 客户端再向服务器发一个确认ACK K+1

  • 只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:

    image

    图1、socket中发送的TCP三次握手

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。

3. HTTP协议

  • 超文本传输协议,基于TCP的面向连接的协议

  • 分为四个步骤:1)建立连接,2)发送请求,3)服务的相应请求,4)客户端接收服务器发回的数据并断开连接

  • 响应状态码:

  • 特点:

    • 简单快速:因为HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
    • 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
    • HTTP 0.9和1.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
    • HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象。
  • 请求方法:

    • GET: 请求获取Request-URI所标识的资源
    • POST: 在Request-URI所标识的资源后附加新的数据
    • HEAD: 请求获取由Request-URI所标识的资源的响应消息报头
    • PUT: 请求服务器存储一个资源,并用Request-URI作为其标识
    • DELETE: 请求服务器删除Request-URI所标识的资源
    • TRACE: 请求服务器回送收到的请求信息,主要用于测试或诊断
    • CONNECT: 保留将来使用
    • OPTIONS: 请求查询服务器的性能,或者查询与资源相关的选项和需求

4. HTTPS协议

  • HTTP的安全版,加入了SSL层

  • HTTPS通信过程:

  • HTTPS与HTTP的区别

    • https协议需要到ca申请证书,一般免费证书很少,需要交费
    • http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议
    • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
    • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全

5.上面知识的总结

  • 首先明确Socket在网络模型中哪里:是应用层与传输层之间的桥梁
  • OSI七层网络模型:

    1. 应用层.
    2. 表示层.
    3. 会话层.
    4. 传输层.
    5. 网络层.
    6. 数据链路层.
    7. 物理层
  • TCP/IP四层网络模型:应用层.传输层.网络层.网络接入层

  • HTTP协议:属于应用层面向对象的协议(超文本传输协议),常基于TCP连接方式, 特点是:

    1. 支持客户/服务端模式
    2. 简单快捷灵活
    3. 客户端发送的每次请求都需要服务器回送响应,请求结束后主动释放连接.俗称”短连接"
  • TCP协议:传输控制协议,提供面向连接.可靠的字节流服务,提供超时重发,丢弃重复数据,检验数据,流量控制等功能。在正式收发数据前,必须建立可靠的连接,即:三次握手.

  • UDP协议:用户数据报协议,面向非连接,不保证可靠性的数据传输服务,没有超时重发等机制,故而传输速度很快.特点:它不与对方建立连接,而是直接就把数据包发送过去, UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境

  • Socket:又称”套接字”,应用程序通过”套接字”向网络发送请求或应答,它是一个针对TCP和UDP编程的接口,借助它建立TCP/UDP连接。socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉

  • HTTP协议—Socket连接--TCP连接关系:

    1. HTTP协议提供了封装或者显示数据的具体形式;
    2. Socket连接提供了网络通信的能力;
    3. TCP连接提供如何在网络中传输;
    4. socket是纯C语言的,跨平台;
    5. HTTP协议是基于socket的,底层使用的就是socket;
    6. 创建Socket连接时,可以指定使用的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
  • TCP和UDP区别

    1. 基于连接和无连接
    2. 对系统资源要求(TCP较多,UDP较少)
    3. UDP程序结构较简单
    4. TCP是流模式,UDP是数据报模式
    5. 可靠性:TCP保证数据正确性,UDP可能丢包,不保证数据准确性

6. WebSocket协议

什么是WebSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

为什么需要 WebSocket

  • HTTP 协议无法实现服务器主动向客户端发起消息
    因为HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求 只能由客户端发起,服务端对请求做出应答处理。
    这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息

    这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)


    image
  • WebSocket的出现是对上面问题的一个解决方案
    WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。
    [站外图片上传中...(image-5d4ed6-1533130904939)]

WebSocket的特点

  1. 最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  2. 建立在 TCP 协议之上,服务器端的实现比较容易。

  3. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

  4. 数据格式比较轻量,性能开销小,通信高效。

  5. 可以发送文本,也可以发送二进制数据。

  6. 没有同源限制,客户端可以与任意服务器通信。

  7. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

      ws://example.com:80/some/path
    

    [站外图片上传中...(image-7622f3-1533130904939)]

WebSocket如何工作

在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

注意:Web浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。
基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。

  1. 创建WebSocket对象
    以下 API 用于创建 WebSocket 对象:

     var Socket = new WebSocket(url, [protocol] );
    

    以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。
    当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

  2. WebSocket 属性
    以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

    属性 描述
    Socket.readyState 只读属性 readyState 表示连接状态,可以是以下值: 0 - 表示连接尚未建立 1 - 表示连接已建立,可以进行通信 2 - 表示连接正在进行关闭 3 - 表示连接已经关闭或者连接不能打开
    Socket.bufferedAmount 只读属性 bufferedAmount表示:已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数
  3. WebSocket 事件
    以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

    事件 事件处理程序 描述
    open Socket.onopen 连接建立时触发
    message Socket.onmessage 客户端接收服务端数据时触发
    error Socket.onerror 通信发生错误时触发
    close Socket.onclose 连接关闭时触发
  4. WebSocket 方法
    以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

    方法 描述
    Socket.send() 使用连接发送数据
    Socket.close() 关闭连接
  5. WebSocket 实例
    WebSocket客户端iOS实现:facebook的框架SRWebSocket

    • 接口

      @interface SRWebSocket : NSObject
      // 构造方法
      - (instancetype)initWithURLRequest:(NSURLRequest *)request;
      // 打开之前设置代理
      @property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
      // Open 
      - (void)open;
      // Close 
      - (void)close;
      // Send a Data
      - (void)sendData:(nullable NSData *)data error:(NSError **)error;
      // Send a UTF8 String
      - (void)sendString:(NSString *)string error:(NSError **)error;
      @end
      
    • 实现代理SRWebSocketDelegate

      @protocol SRWebSocketDelegate <NSObject>
      @optional
      - (void)webSocketDidOpen:(SRWebSocket *)webSocket;
      - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string;
      - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data;
      
      - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
      - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean;
      @end
      

7. MQTT协议

1. 什么是MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用

2. MQTT协议的主要特征

MQTT协议工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:

  • 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合

      这一点很类似于XMPP,但是MQTT的信息冗余远小于XMPP,,因为XMPP使用XML格式文本来传递数据
    
  • 对负载内容屏蔽的消息传输

  • 使用TCP/IP提供网络连接

      主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN。
      这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。
    
  • 有三种消息发布服务质量QoS:

    • 0 代表“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
    • 1 代表“至少一次”,确保消息到达,但消息重复可能会发生。
    • 2 代表“只有一次”,确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次。
  • 小型传输,开销很小(固定长度的头部是2字节),协议交换最小化,以降低网络流量

      这就是为什么在介绍里说它非常适合“在物联网领域,传感器与服务器的通信,信息的收集”,要知道嵌入式设备的运算能力和带宽都相对薄弱,使用这种协议来传递消息再适合不过了
    
  • 使用Last Will和Testament特性通知有关各方客户端异常中断的机制

      Last Will:即遗言机制,用于通知同一主题下的其他设备发送遗言的设备已经断开了连接。
      Testament:遗嘱机制,功能类似于Last Will
    

3. MQTT协议原理

MQTT协议实现方式

实现MQTT协议需要客户端和服务器端协同完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)
(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

网络传输与应用消息

MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。
当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。

MQTT客户端

一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端的行为有:

  • 发布其他客户端可能会订阅的信息
  • 订阅其它客户端发布的消息
  • 退订或删除应用程序的消息
  • 断开与服务器连接。
MQTT服务器

MQTT服务器以称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它的行为有:

  • 接受来自客户的网络连接
  • 接受客户发布的应用信息
  • 处理来自客户端的订阅和退订请求
  • 向订阅的客户转发应用程序消息
MQTT协议中的订阅、主题、会话
  1. 订阅(Subscription)
    订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。

  2. 会话(Session)
    每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。

  3. 主题名(Topic Name)
    连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。

  4. 主题筛选器(Topic Filter)
    一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。

  5. 负载(Payload)
    消息订阅者所具体接收的内容。

MQTT协议中的方法

MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有:

  • Connect。等待与服务器建立连接。
  • Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
  • Subscribe。等待完成订阅。
  • UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。
  • Publish。MQTT客户端发送消息请求,发送完成后返回应用程序线程。

4. MQTT协议数据包结构

在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。MQTT数据包结构如下:

  • 固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
  • 可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
  • 消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。
MQTT固定头

固定头存在于所有MQTT数据包中,它包括:

  • 固定报头的格式
    [图片上传失败...(image-3a5de9-1533130904939)]

  • MQTT数据包类型
    位置:第1个字节,二进制位7-4。
    表示为4位无符号值,这些值的定义见 表格 2.1 -控制报文的类型

  • 标识位
    位置:Byte 1中bits 3-0。这些值的定义见表格2.2
    在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接:

    • DUP:发布消息的副本。用来在保证消息的可靠传输,如果设置为1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送

    • QoS:发布消息的服务质量,即:保证消息传递的次数

        Ø00:最多一次,即:<=1
        Ø01:至少一次,即:>=1
        Ø10:一次,即:=1
        Ø11:预留
      
    • RETAIN: 发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放

MQTT可变头

MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。可变头的内容因数据包类型而不同,较常的应用是作为包的标识:
很多类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。

Payload消息体

Payload消息体位MQTT数据包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息:

  • (1)CONNECT,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。
  • (2)SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS。
  • (3)SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
  • (4)UNSUBSCRIBE,消息体内容是要订阅的主题。

5. MQTT客户端的iOS实现

  • 说明
    MQTT基于订阅者模型架构,客户端如果互相通信,==必须在同一订阅主题下==,即都订阅了同一个topic,客户端之间是没办法直接通讯的。订阅模型显而易见的好处是群发消息的话只需要发布到topic,所有订阅了这个topic的客户端就可以接收到消息了
    发送消息必须发送到某个topic,重点说明的是不管客户端是否订阅了该topic都可以向topic发送了消息,还有如果客户端订阅了该主题,那么自己发送的消息也会接收到

  • MQTTClient的使用

    • 建立连接
      和服务器端确定好MQTT服务器地址,端口号, 用户名, 密码, 订阅主题topic
    /**
          host: 服务器地址
          port: 服务器端口
          tls:  是否使用tls协议,mosca是支持tls的,如果使用了要设置成true
          keepalive: 心跳时间,单位秒,每隔固定时间发送心跳包, 心跳间隔不得大于120s
          clean: session是否清除,这个需要注意,如果是false,代表保持登录,
          如果客户端离线了再次登录就可以接收到离线消息
          auth: 是否使用登录验证
          user: 用户名
          pass: 密码
          willTopic: 订阅主题
          willMsg: 自定义的离线消息
          willQos: 接收离线消息的级别
          clientId: 客户端id,需要特别指出的是这个id需要全局唯一,因为服务端是根据这个来区分不
          同的客户端的,默认情况下一个id登录后,假如有另外的连接以这个id登录,
          上一个连接会被踢下线, 我使用的设备UUID
    */
    NSString *clientId = [UIDevice currentDevice].identifierForVendor.UUIDString;
    MQTTSessionManager *sessionManager = [[MQTTSessionManager alloc] init];
    [sessionManager connectTo:@"121.199.19.126"  
                           port:1883
                            tls:false
                      keepalive:60  //心跳间隔不得大于120s
                          clean:true
                           auth:true
                           user:@"guest"
                           pass:@"guest"
                           will:false
                      willTopic:nil
                        willMsg:nil
                        willQos:0
                 willRetainFlag:false
                   withClientId:clientId];
        sessionManager.delegate = self;
        self.sessionManager = sessionManager;
    
    • 监控连接状态
      连接当前状态,添加对应的回调接口,可以进行相关的业务逻辑处理。
    // 添加监听状态观察者
    [self.sessionManager addObserver:self
                       forKeyPath:@"state"
                          options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                          context:nil];
    
    • 监听连接状态,进行相应处理
    // 监听当前连接状态
    
  • (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    switch (self.sessionManager.state) {
    case MQTTSessionManagerStateClosed:
    NSLog(@"连接已经关闭");
    break;
    case MQTTSessionManagerStateClosing:
    NSLog(@"连接正在关闭");

          break;
      case MQTTSessionManagerStateConnected:
          NSLog(@"已经连接");
    
          break;
      case MQTTSessionManagerStateConnecting:
          NSLog(@"正在连接中");
    
          break;
      case MQTTSessionManagerStateError: {
          NSString *errorCode = self.sessionManager.lastErrorCode.localizedDescription;
          NSLog(@"连接异常 ----- %@",errorCode);
      }
    
          break;
      case MQTTSessionManagerStateStarting:
          NSLog(@"开始连接");
         break;
      default:
          break;
    

    }
    }

    
    * 接收消息 
        实现MQTTSessionManagerDelegate代理方法,处理数据 
        
        ```
        // 获取服务器返回数据
        - (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained {
            NSLog(@"------------->>%@",topic);
         
            NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@",dataString);
         
             // 进行消息处理
        }
        ```
    
    * 订阅和发送消息
    
    

    // 订阅主题 NSDictionary类型,Object 为 QoS,key 为 Topic
    self.sessionManager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:MQTTQosLevelExactlyOnce] forKey:@"hello"];

    // 发送消息 返回值msgid大于0代表发送成功
    NSString *msg = @"hahaha";
    UInt16 msgid = [self.sessionManager sendData:[msg dataUsingEncoding:NSUTF8StringEncoding] //要发送的消息体
    topic:@"hello" //要往哪个topic发送消息
    qos:0 //消息级别

    
    

参考文章:MQTT - iOS实现

相关文章

  • iOS搭建Socket服务器的相关方法

    iOS网络编程层次 iOS网络编程层次结构也分为三层: Cocoa层:NSURL,Bonjour,Game Kit...

  • Socket

    Socket iOS网络编程层次结构 iOS网络编程层次结构分为三层,从上往下依次为: Cocoa层:NSURL,...

  • iOS关于HTTP协议和网络编程

    1.网络编程 1>什么是网络编程? 网络编程,是我们iOS程序开发者针对网络模块进行得代码编程,是作为一个资深开发...

  • IOS网络编程

    IOS网络编程 NSURLConnection NSURLSession是NSURLConnection 的替代者...

  • 网络相关以及TCP/IP协议

    一、iOS9 网络编程的重大改变: 1、网络请求方式的改变 1)NSURLConnection: iOS9之前使用...

  • iOS网络编程

    iOS网络相关类介绍 网络请求地址对象——NSURL url 介绍url,统一资源定位符,也被称为网址,因特网上标...

  • iOS网络编程

    网络编程 1. 概论 建立连接:通过IP或者域名来连接两台设备,通过端口号找到对应的通信程序 通信协议:要传输的数...

  • iOS网络编程

    一、URL URL的全称是Uniform Resource Locator(统一资源定位符),通过1个URL,能找...

  • iOS 网络编程

    网络层次简介 网络七层由下往上分别为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。其中物理层、数据链...

  • IOS网络编程

    现在APP是无网络不APP,现在很少有单机的APP了,所以任何开发语言你都得和网络接触.像我这种之前没有编程经验的...

网友评论

    本文标题:iOS网络编程

    本文链接:https://www.haomeiwen.com/subject/wfkdvftx.html