• Zephyr网络协议


     // 本文部分内容来自网络

    1. 网络基础知识

            OSI七层模型与TCP/IP模型对应关系

    HTTPS与HTTP

    HTTPS 协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。 HTTPS 和 HTTP 的区别主要如下:

    • HTTPS 协议使用 ca 申请证书,由于免费证书较少,需要一定费用。
    • HTTP 是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
    • HTTP 和 HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

    SSL(Secure Sockets Layer 安全套接层)及其继任者传输层安全(TransportLayer Security,TLS & Datagram Transport Layer Security,DTLS)是为网络通信提供安全及数据完整性的一种安全协议。DTLS、TLS与SSL在传输层对网络连接进行加密,DTLS在UDP传输协议之上,而SSL/TLS则在TCP传输协议之上。

    一般来说,HTTPS 主要用途有三个:一是通过证书等信息确认网站的真实性;二是建立加密的信息通道;三是数据内容的完整性。

    SSL证书保证了网站的唯一性与真实性;

    签发证书的 CA 中心会发布一种权威性的电子文档——数字证书,它可以通过加密技术(对称加密与非对称加密)对我们在网上传输的信息进行加密。

    当数据包经过无数次路由器转发后会发生数据劫持,黑客将数据劫持后进行篡改,比如植入广告;开启HTTPS后黑客就无法对数据进行篡改,就算真的被篡改了,我们也可以检测出问题

    TCP报文格式


    TCP封装在IP报文中的时候,如下图所示,TCP头紧接着IP头(IPV6有扩展头的时候,则TCP头在扩展头后面),不携带选项(option)的TCP头长为20bytes,携带选项的TCP头最长可到60bytes。

     

    TCP的源端口、目的端口、以及IP层的源IP地址、目的IP地址四元组唯一的标识了一个TCP连接

    TCP各字段释义:

    TCP源端口(Source Port):16位的源端口其中包含发送方应用程序对应的端口。源端口和源IP地址标示报文发送端的地址。

    TCP目的端口(Destination port):16位的目的端口域定义传输的目的。这个端口指明报文接收计算机上的应用程序地址接口。

    TCP序列号(SequenceNumber):32位的序列号标识了TCP报文中第一个byte在对应方向的传输中对应的字节序号。当SYN出现,SN=ISN(随机值)单位是byte。比如发送端发送的一个TCP包净荷(不包含TCP头)为12byte,SN为5,则发送端接着发送的下一个数据包的时候,SN应该设置为5+12=17。通过序列号,TCP接收端可以识别出重复接收到的TCP包,从而丢弃重复包,同时对于乱序数据包也可以依靠系列号进行重排序,进而对高层提供有序的数据流。另外如果接收的包中包含SYN或FIN标志位,逻辑上也占用1个byte,应答号需加1。

    TCP应答号(Acknowledgment  Number简称ACK Number):32位的ACK Number标识了报文发送端期望接收的字节序列。如果设置了ACK控制位,这个值表示一个准备接收的包的序列码,注意是准备接收的包,比如当前接收端接收到一个净荷为12byte的数据包,SN为5,则会回复一个确认收到的数据包,如果这个数据包之前的数据也都已经收到了,这个数据包中的ACK Number则设置为12+5=17,表示之前的数据都已经收到了,准备接受SN=17的数据包。

    注:关于TCP序列号和应答号,也可以参考文章:http://blog.csdn.net/a19881029/article/details/38091243/

    头长(Header Length):4位包括TCP头大小,指示TCP头的长度,即数据从何处开始。header length字段由4比特构成,最大值为15,单位是32比特,即头长的最大值为15*32 bits = 60bytes,因此上面说携带选项的TCP头长最长为60bytes。

    保留(Reserved):4位值域,这些位必须是0。为了将来定义新的用途所保留,其中RFC3540将Reserved字段中的最后一位定义为Nonce标志。后续拥塞控制部分的讲解我们会简单介绍Nonce标志位。

    标志(Code Bits):8位标志位,下面介绍。

    窗口大小(Window Size):16位,该值指示了从Ack Number开始还愿意接收多少byte的数据量,也即用来表示当前接收端的接收窗还有多少剩余空间,用于TCP的流量控制。

    校验位(Checksum):16位TCP头。发送端基于数据内容计算一个数值,接收端要与发送端数值结果完全一样,才能证明数据的有效性。接收端checksum校验失败的时候会直接丢掉这个数据包。CheckSum是根据伪头+TCP头+TCP数据三部分进行计算的。

    优先指针(紧急,Urgent  Pointer):16位,指向后面是优先数据的字节,在URG标志设置了时才有效。如果URG标志没有被设置,紧急域作为填充。 选项(Option):长度不定,但长度必须以是32bits的整数倍。常见的选项包括MSS、SACK、Timestamp等等。

    八位标志位分别介绍如下:

    CWR(Congestion Window Reduce):拥塞窗口减少标志set by sender,用来表明它接收到了设置ECE标志的TCP包。并且sender 在收到消息之后已经通过降低发送窗口的大小来降低发送速率。

    ECE(ECN Echo):ECN响应标志被用来在TCP3次握手时表明一个TCP端是具备ECN功能的。在数据传输过程中也用来表明接收到的TCP包的IP头部的ECN被设置为11。注:IP头部的ECN被设置为11表明网络线路拥堵。

    注:关于CWR和ECE标记为详细信息可参考:http://www.cnblogs.com/hadis-yuki/p/5467787.html

    URG(Urgent):该标志位置位表示紧急(The urgent pointer) 标志有效。该标志位目前已经很少使用参考后面流量控制和窗口管理部分的介绍。

    ACK:取值1代表Acknowledgment Number字段有效,这是一个确认的TCP包,取值0则不是确认包。后续文章介绍中当ACK标志位有效的时候我们称呼这个包为ACK包,使用大写的ACK称呼。

    PSH(Push):该标志置位时,一般是表示发送端缓存中已经没有待发送的数据,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。

    RST(Reset):用于reset相应的TCP连接。通常在发生异常或者错误的时候会触发复位TCP连接。

    SYN:同步序列编号(Synchronize Sequence Numbers)有效。该标志仅在三次握手建立TCP连接时有效。

    FIN(Finish):No more data from sender。当FIN标志有效的时候我们称呼这个包为FIN包。

    UDP报文格式

    u2字节源端口字段

    源端口是一个大于1023的16位数字,由基于UDP应用程序的用户进程随机选择。

    u2字节节的端口字段

    u2字节长度字段

    指明了包括首部在内的UDP报文段长度。UDP长字段的值是UDP报文头的长度(8字节)与UDP所携带数据长度的总和。

    u2字节校验和字段

    是指整个UDP报文头和UDP所带的数据的校验和(也包括伪报文头)。伪报文头不包括在真正的UDP报文头中,但是它可以保证UDP数据被正确的主机收到了。因在校验和中加入了伪头标,故ICMP除能防止单纯数据差错之外,对IP分组也具有保护作用。

    源端口号和目的端口号如上和TCP的相同。

    UDP长度:UDP报文的字节长度(包括首部和数据)。

    UDP校验和: 检验UDP首部和数据部分的正确性。

    IP报文格式

    版本:指IP协议的版本。

    首部长度:首部的长度

    服务类型:如下图:

    其中优先级用来区别优先级别不同的IP报文。 D表示要求有更低的时延。 T表示要求有更高的吞吐量。 R表示要求有更高的可靠性。

    总长度:报文的长度。

    标识:由于数据报长度超过传输网络的MTU(最大传输单元)而必须分片,这个标识字段的值被复制到所有数据报分片的标识字段中,使得这些分片在达到最终的目的地时可以依照标识字段的内容重新组成原先的数据报。

    标志:最低位是MF,MF=1时,表示后面还有分片。中间位的DF,DF=1时,表示不能分片。

    片偏移: 和前面的数据分片相关,是本分片在原先数据报文中相对首位的偏移位。

    生存时间:数据报在网络中存活的时间,所允许通过的路由器的最大数量,没通过一个路由器,该值自动减一,如果数值为0,路由器就可以把该数据报丢弃。

    协议: 指出数据报携带的数据是使用何种协议,以便目的主机的IP层能知道次数据报上交到哪一个进程(不同协议有一个专门不同的进程处理)。

    首部校验位和:对首部进行校验运算。校验方法 : 在发送端,将IP数据报首部划分为多个16位的二进制序列,并将首部校验和字段置为0,用反码运算将所有16位序列对位相加后,将得到多的 和的反码写入首部校验和字段。接收端接收到数据报后,将数据报首部的所有字段组织成多个16位的二进制序列,再使用反码运算相加 一次,将得到的结果取反。如果结果为0代表没出错,否则出错。

    源地址:发送数据报的节点地址。

    目的地址:接受数据报的节点地址。

    ICMP协议

    ICMP是网络层的补充,可以回送报文。用来检测网络是否通畅Ping命令就是发送ICMP的echo包,通过回送的echo relay进行网络测试。

    一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过"IP地址+端口号"来区分不同的服务的。

    服务器一般都是通过知名端口号来识别的,如图2-2所示。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69。任何TCP/IP实现所提供的服务都用知名的1~1023之间的端口号。这些知名端口号由Internet号分配机构(Internet Assigned Numbers Authority,IANA)来管理。

    常见的应用层协议和传输层协议之间的关系:

    • HTTP默认使用TCP的80端口标识
    • FTP默认使用TCP的21端口标识
    • SMTP默认使用TCP的25端口标识
    • POP3默认使用TCP的110端口
    • HTTPS默认使用TCP的443端口
    • DNS使用UDP的53端口
    • 远程桌面协议(RDP)默认使用TCP的3389端口
    • Telnet使用TCP的23端口
    • Windows访问共享资源使用TCP的445端口

    Socket套接字接口参数

    创建一个socket
    #include <sys/socket.h>
    int socket( int af, int type, int protocol);
    af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
    type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
    protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

    sock_stream 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。
    sock_dgram 是无保障的面向消息的socket , 主要用于在网络上发广播信息。
    SOCK_STREAM是基于TCP的,数据传输比较有保障。SOCK_DGRAM是基于UDP的,专门用于局域网,基于广播SOCK_STREAM
     是数据流,一般是tcp/ip协议的编程,SOCK_DGRAM分是数据抱,是udp协议网络编程

    AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信。
    选择 AF_INET 的目的就是使用 IPv4 进行通信。因为 IPv4 使用 32 位地址,相比 IPv6 的 128 位来说,计算更快,便于用于局域网通信。
    而且 AF_INET 相比 AF_UNIX 更具通用性,因为 Windows 上有 AF_INET 而没有 AF_UNIX。

    AF 表示ADDRESS FAMILY 地址族
    PF 表示PROTOCL FAMILY 协议族

    TCP三次握手

    TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)Sequence number(顺序号码) Acknowledge number(确认号码)第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。

    ARP MAC地址解析

    数据包经常通过以太网发送,在发送数据包的过程中先封装IP的帧头信息然后再封装链路层的MAC地址,最后是以太网的帧头信息。以太网的帧头包括:目标地址、源地址、帧的类型,以太网设备的地址是48位的,它不识别32位的IP地址(IPV4)只识别MAC地址。因为以太网只识别设备的MAC地址,那么问题就来了,源地址我们是知道的就是主机A的MAC地址,那目标的MAC地址呢?我们可能知道也可能不知道。怎样得到目标的MAC地址呢?地址解析协议会完成IP地址和MAC地址之间的映射得到主机B的MAC地址,这是地址解析协议的绝活。

    首先主机A根据路由表的内容确定B主机的IP地址,然后在本主机上查询ARP缓存的ARP表看看有没有和B主机IP地址对应的MAC地址的历史记录,如果有就添加到以太网帧头的目标地址。如果在ARP表中没有与B主机IP对应的MAC地址,主机A就会向本地网络中的所有主机广播发送ARP请求帧(包含B主机的IP地址,A主机的IP地址和MAC地址),接收到ARP请求的各主机将收到的请求帧中的目标IP地址比较,如果与自己的IP地址不匹配丢弃ARP请求,如果B主机匹配B主机将自己的IP地址和MAC地址回复给发送请求的A主机,同时B主机在ARP表中更新或者记录主机A的IP地址和MAC地址。

    A主机在收到数据时首先判断帧的类型,可以看到收到的数据可能是IP数据也可能是ARP数据。如果收到的是IP数据就会按IP的数据帧格式进行解算,如果收到的是ARP数据帧再判断是响应帧还是请求帧,如果是响应帧A主机就会更新本地的ARP表,若是请求帧就会把A主机的IP地址和MAC地址回复给发送请求的主机

     2. Zephry网络架构

    Zephyr通过合并Contiki RTOS开发了一个原生的、优化过的轻量级TCP/IP协议栈(名为uIP,它实现了RFC兼容的IPv4、IPv6、TCP和UDP)。此外,Zephyr还包括一个完整的蓝牙低功耗控制器和一个基于TinyCrypt的加密库,以及支持MQTT和SSL。

    ######### http ##############
     ============================
      http_client_send_req
      
      http_request
      
      http_send_flush
      
      http_send_msg_raw
      
     ######### net context #######
     ==============================
      net_app_send_pkt
     
      ctx->send_data
      
      net_context_sendto
      
      sendto
      
      send_data
      
     ######### tcp/udp #######
     ==============================
      net_tcp_send_head
      
      net_tcp_send_pkt
     
     ######### ip #######
     ==============================
      net_send_data
      
      net_if_send_data
      
      iface->l2->send
      
     ######### l2(netif) #######
     ==============================
      dummy_send
     
      net_if_queue_tx
      
      k_fifo_put(&iface->tx_queue, pkt)
      
      net_if_tx(k_fifo_get(&iface->tx_queue, K_NO_WAIT);)
      
      api->send
      
      xxx_tx(mdm)
     
      xxx_send_data
     
      xxx_msg_send_ceaselessly
     
      xxx_netif_msg_send
     
     ######### phy #######
     ==============================
      icp_channel_send
    

    Zephyr UDP客户端发送接收接口使用方式示意

    • 创建socket

    net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context);

    用于创建socket,信息保存在 struct net_context context 中

    • 绑定本地端口(也可指定绑定IP)

    net_context_bind(context, (struct sockaddr *)&my_addr6);

    • 设置接收数据包接口

    net_context_recv(context, udp_received, 0, &users_data);

    指定接收函数为udp_received,用户提供。当收到外部发送来的数据包时,匹配IP及端口号后,内核从接收线程中调用udp_received。用户根据不同情况可有两种选择,当数据业务简单且无阻塞操作时,可直接在接收线程中完成对数据包的解析工作。当数据业务复杂或者解析时有阻塞操作时,则应该将数据包传递给应用线程去做,该回调仅仅作为数据传递使用。传递数据包可参考使用全局变量、内核提供的链表、fifo、mailbox、ringbuffer等,也可用户自行决定方式。

    •  获取发送数据包

    send_pkt = net_pkt_get_tx(context, K_FOREVER);

    获取数据包管理结构

     

    net_pkt_get_data

    获取数据data

     

    net_pkt_append_all(send_pkt, expecting_len, data,K_FOREVER);

    将数据data拷贝到数据包中struct net_pkt * send_pkt中

    • 发送数据包接口

    net_context_sendto(send_pkt, &dst_addr,addrlen, udp_sent, 0, len, proto);

    • 释放socket

    net_context_put(context);

    Zephyr TCP客户端发送接收接口使用方式示意

    struct net_context *context;

    • 创建socket

    net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP,tcp_recv4);

    用于创建socket,信息保存在 struct net_context tcp_recv4中

    • 绑定本地端口和IP

    net_context_bind(*tcp_recv4,(struct sockaddr *)&my_addr4, sizeof(struct sockaddr_in));

    •  调用tcp连接接口

    net_context_connect(tcp_send,(struct sockaddr *)&peer_addr4,

    sizeof(peer_addr4),tcp_connected,K_FOREVER,UINT_TO_POINTER(AF_INET));

    若连接成功,内核会返回成功值并调用tcp_connected接口通知已经建链。

    • 设置接收数据包接口

    net_context_recv(tcp, cb, 0, user_data);

    在net_context_connect成功后调用,推荐在tcp_connected中设置。原理同4.3.

    • 获取发送数据包

    send_pkt = net_pkt_get_tx(context, K_FOREVER);

    获取数据包管理结构

     

    send_frag = net_pkt_get_data(context, K_FOREVER);

    获取数据data

     

    net_pkt_frag_add(send_pkt, send_frag);

    将数据data信息插入pkt

     

    net_pkt_append_all(send_pkt, expecting_len, data,K_FOREVER);

    将数据data拷贝到数据包中struct net_pkt * send_pkt中

    • 发送数据包接口

    net_context_send(send_pkt, tcp_sent, 0, len, proto);

    • 释放socket

    net_context_put(context);

     

     

    net_pkt_get_data最终调用内存管理接口申请内存:

    net_pkt_get_data
    	_pkt_get_data  
    	  net_pkt_get_reserve_data
    		net_pkt_get_reserve_data
    			net_buf_alloc
    				net_buf_alloc_len
    					data_alloc
    						pool->alloc->cb->alloc(buf, size, timeout)
    							heap_data_alloc
    
    
    
    #define net_buf_alloc(_pool, _timeout)  net_buf_alloc_len(_pool, CONFIG_NET_BUF_DATA_SIZE ,_timeout);
    
    #define CONFIG_NET_BUF_DATA_SIZE 128
    
    也就是说,每次申请数据data的buffer都是固定128字节大小
    
    net_pkt_append_all
    	net_pkt_append
    		net_pkt_append_bytes
    			
    
    static inline u16_t net_pkt_append_bytes(struct net_pkt *pkt,
    					 const u8_t *value,
    					 u16_t len, s32_t timeout)
    {
    	struct net_buf *frag = net_buf_frag_last(pkt->frags);
    	u16_t added_len = 0;
    
    	do {
    		u16_t count = min(len, net_buf_tailroom(frag));
    		void *data = net_buf_add(frag, count);
    
    		memcpy(data, value, count);
    		len -= count;
    		added_len += count;
    		value += count;
    
    		if (len == 0) {
    			return added_len;
    		}
    
    		frag = net_pkt_get_frag(pkt, timeout);
    		if (!frag) {
    			return added_len;
    		}
    
    		net_pkt_frag_add(pkt, frag);
    	} while (1);
    
    	/* Unreachable */
    	return 0;
    }
    			
    

    net_context_connect:完成TCP三次握手,最终建立连接

    1.net_tcp_register注册接收到ack包后的回调接口

       ret = net_tcp_register(addr,
              laddr,
              ntohs(rport),
              ntohs(lport),
              tcp_synack_received,
              context,
              &context->conn_handler);

    2. 发送syn包

      send_syn(context, addr);

    3. 收到ack包后进入tcp_synack_received函数,该函数首先注册收到数据后的回调接口tcp_established,然后发送ack包

        ret = net_tcp_register(&remote_addr,
               &local_addr,
               ntohs(tcp_hdr->src_port),
               ntohs(tcp_hdr->dst_port),
               tcp_established,
               context,
               &context->conn_handler);

      send_ack(context, &remote_addr, false);

     相关配置

    socket相关的总连接个数(TCP+UDP+ICMP等连接总个数):

    CONFIG_NET_MAX_CONN TCPIP连接管理结构体conn个数

    CONFIG_NET_MAX_CONTEXTS 对接socket的context个数

    说明:如需要修改连接个数,这两个值都要修改,而且个数要一致。Dns功能需要占用一个连接,所以留给客户可使用的连接个数为“CONFIG_NET_MAX_CONTEXTS-1”。

    POLL个数:

    CONFIG_NET_SOCKETS_POLL_MAX socket poll的个数,必须大于等于应用用的poll的个数,且不能大于“总连接个数”

    TCP服务端连接个数 :

    CONFIG_NET_TCP_BACKLOG_SIZE tcp服务端管理数组,必须大于等于tcp当服务端的连接

    TCP时间配置

    CONFIG_NET_SOCKETS_CONNECT_TIMEOUT tcp连接超时时间,单位秒。超过该时间tcp握手连接失败

    CONFIG_NET_TCP_ACK_TIMEOUT 等待对端ACK的超时时间。用于做服务端发送SYN_ACK后等对端ACK,正常情况关闭过程FIN等待对端ACK。 CONFIG_NET_TCP_INIT_RETRANSMISSION_TIMEOUT tcp首次重传时间,以后重传时间每次会翻倍。修改此值会减小由于网络慢导致的重传次数过多,适用于网络延时大的情况。该值可以通过NV配置,如:“AT+ZNVSET="tcp_retran_time",2000”,表示首次重传时间为2000毫秒,该功能重启生效。

    CONFIG_NET_TCP_RETRY_COUNT tcp重传次数,超过该重传次数内核会断开连接

    内存使用上限配置相关

    CONFIG_NET_PKT_RX_COUNT 接收数据包头缓存个数 决定可以缓存多少个IP包

    CONFIG_NET_PKT_TX_COUNT 发送数据包头缓存个数 

    CONFIG_NET_BUF_RX_COUNT 接收数据包数据段个数 决定可以缓存多少个IP数据包数据段,每个大小为CONFIG_NET_BUF_DATA_SIZE

    CONFIG_NET_BUF_TX_COUNT 发送数据包数据段个数 

    CONFIG_NET_BUF_DATA_SIZE 每个数据包数据段的大小 单个IP数据包数据段的大小

    修改原则:

    发送多包小数据,改大CONFIG_NET_PKT_TX_COUNT;

    接收多包小数据,改大CONFIG_NET_PKT_RX_COUNT。

    发送少包大数据,改大CONFIG_NET_BUF_TX_COUNT;

    接收少包大数据,改大CONFIG_NET_BUF_RX_COUNT。

    发送多包大数据,改大CONFIG_NET_PKT_TX_COUNT和CONFIG_NET_BUF_TX_COUNT;

    接收大包大数据,改大CONFIG_NET_PKT_RX_COUNT和CONFIG_NET_BUF_RX_COUNT。

    改小的情况请以此类推。

     

     

    高层协议

    高层协议指TCPUDP之上的协议,有时应用传输数据时,会在底层协议之上加载上层协议,比如http、caop等。有时,高层协议会提供socket封装接口,向用户屏蔽socket层的具体调用实现,不同的操作系统情况不一样,在zephyr中,http接口实现了该类封装,当需要使用socket向外发送数据时,只需调用

    http_client_init(&http_ctx, SERVER_ADDR, SERVER_PORT);即可,socket由该接口自动创建并供用户使用。发送数据调http_client_send_req(ctx, &req, NULL, result, sizeof(result),NULL, APP_REQ_TIMEOUT);即可

    coap协议并没有提供这样的封装接口,需要按标准方式调用。

    主要接口函数:

    int http_client_init(struct http_ctx *ctx,

              const char *server,

              u16_t server_port,

              struct sockaddr *server_addr,

              s32_t timeout);

    发送数据前,先通过http_client_init()初始化http client,ctx是当前http链接的上下文;server是字符串形式的服务器ip地址或域名(传入域名时必须保证dns解析可用);server_port是服务器监听的端口;server_addr是struct sockaddr形式的ip地址,可以为NULL,为NULL时将使用server作为服务器地址;timeout是域名解析的超时时间。

     

    int http_client_send_req(struct http_ctx *ctx,

               struct http_request *req,

               http_response_cb_t cb,

               u8_t *response_buf,

               size_t response_buf_len,

               void *user_data,

               s32_t timeout);

    使用http_client_send_req()发送数据,ctx是当前http链接的上下文;req是发送请求的具体信息和数据(数据结构见上面的重要结构体);cb是应用接收返回的数据的回调;response_buf是应用接收数据存放的空间,需要应用提前申请好空间;response_buf_len是response_buf的长度;user_data设为NULL;timeout是等待服务器回复的超时时间。注意这里的cb是应用主要处理服务器返回的数据的函数,需要应用根据其业务实现。

     

    int net_app_close(struct net_app_ctx *ctx);

    释放http链接,ctx是当前net_app链接的上下文,struct http_ctx中有这个struct net_app_ctx。

        

         Accept、Accept-Charset、Accept-Encoding、Accept-language,这些项直接填到struct http_request 中的header_fields里,应用自己解析所指示的可接受的数据内容、字符集、编码方式、语言等。服务器返回的 status code 直接传给应用,由应用来判断成功还是失败。对于301、302等重定向的返回码,由应用来处理

    http接口中对Socket的封装
    
    http_client_init
    	net_app_init_tcp_client
    		net_app_init_client
    			_net_app_config_local_ctx
    				setup_ipv4_ctx
    					net_context_get
    			bind_local
    				_net_app_set_net_ctx
    					net_context_bind
    
    /* TCP recv callback is set after we have accepted the* connection.*/
    
    
    http_client_send_req
    	net_app_connect
    		http_request
    			http_prepare_and_send
    				get_net_pkt
    					net_app_get_net_pkt_with_dst
    						net_pkt_get_tx
    						
    				net_pkt_append
    					net_pkt_get_frag
    						net_pkt_get_reserve_data
    					net_pkt_frag_add
    
    • TLS 建立于TCP可靠的传输机制之上,而DTLS基于UDP,必须自建保障机制:  DTLS 必须检测MTU大小,当应用层数据包超过时报错;  为防止握手的IP数据包超载导致丢失,DTLS 针对握手消息实现fragment处理。

    • TLS 在传输出错时会中断连接,而DTLS需兼容多种出错场景,出错时往往直接丢弃处理;

    • DTLS不支持RC4流加密算法。
  • 相关阅读:
    JavaScript 简单说明(一)
    把mysql5.7源码包生成rpm包
    CentOS下pythonsqlite3安装
    No module named 'MySQLdb' python3.6 + django 1.10 + mysql 无法连接
    python3不再支持mysqldb 请用pymysql和mysql.connector
    元素的自定义特性
    获取元素的页面位置,兼容各浏览器
    通过正则格式化url查询字符串
    borderimage属性在chrome中的不同效果
    获取页面元素的滚动位置,兼容各浏览器
  • 原文地址:https://www.cnblogs.com/DF11G/p/9802498.html
Copyright © 2020-2023  润新知