iOS平台Socket编程实践:
关于TCP Socket编程基础可以参看我的《我所不知道的TCP Socket编程》系列文章;
iOS平台Socket编程主要内容及辅助工具:
1.TCP协议编程;
2.UDP协议编程;
3.WireShark抓包辅助分析;(www.wireshark.org)
网路基础薄弱的同学可以看下《计算机网络协议原理基础课程视频》
Socket简介:
Socket就是一个插座;
BSD/Unix最早实现TCP/IP协议编程;
iOS Socket编程常用接口:
·BSD Socket;-操作系统内核暴露出来的接口;
·CocoaAsyncSocket;-OC中的CFNetworking提供了相应的编程接口,基于此的一个开源的框架CocoaAsyncSocket;
Socket编程接口:
1)是系统协议栈(传输层)提供给应用层的接口;
2)可以进行TCP/UDP协议(他们是传输层协议)的应用编程;
3)Socket也是进程间通讯的一种方式;
注:有需要的同学可以复习下《计算机系统》《操作系统》
Socket(套接字)编程:
什么是套接字?
Berkeley套接字接口API,使用一个Internet套接字的概念,使主机或者一台计算机上的进程间进行通信;
他可以在很多不同的输入/输出设备和驱动上运行,尽管这依赖于操作系统的具体实现;
接口实现用于TCP/IP协议,如今,所有现代操作系统都有一些源于Berkeley套接字的实现,他已经成为连接Internet的标准编程接口;
所以,现代大部分操作系统,底层都是这一接口来进行网络编程,因为大家都遵循POSIX(可移植操作系统)和。。。的规范;
Socket C语言接口:
·前提:
有较好的C语言基础,熟悉TCP/UDP/IP协议原理;
·Socket到底是什么?
是一种标准Unix文件描述符(int)来与其他程序通讯的方式,在类Unix操作系统时,一切皆文件;
所以Socket也不例外,就是说Unix程序进行任何类型的I/O操作,都是通过读写文件描述符实现的;一个文件描述符就是一个整数,但是这个整数和一个以打开的文件关联起来了;这个所谓的文件可能是一个网络接口,一个FIFO,一个管道,一个终端,一个真正存于磁盘的文件,或是其他东西;
在系统里,Socket描述符也是一个文件描述符,是一个整形变量;
可以通过系统调用socket原型(函数),获取一个网络Socket描述符;
int socket(int domain, int type, int protocol);
系统调用;
Socket有两个大类:1.Stream(TCP是流) 2.DataGram(UDP是数据报)
网络编程基础:
IP地址,端口,字节序:
IPv4地址:32位4字节表示;
IPv6地址:128位16个字节表示;
端口号:16位2个字节表示(最大65535);
TCP和UDP的端口号是互不影响的;端口是传输层,TCP/UDP是网络层,网络层向传输层指定端口号,TCP和UDP端口号可以一样;
字节序:TCP/IP协议族里规定,网络字节序里使用大端字节序(高位字节排放在内存的低地址端);
系统提供了解决这类问题的函数(大小端转换 高低地址的翻转);
注:
UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的;
字节序转换函数:(man手册)
htons() : host to nerwork short 双字节的数 从 主机字节序 转 网络字节序
htonl() : host to nerwork long 四字节的数 从 主机字节序 转 网络字节序
ntohs() : short to nerwork host 双字节的数 从 网络字节序 转 主机字节序
ntohl() : long to nerwork host 四字节的数 从 网络字节序 转 主机字节序
CocoaAsyncSocket:
iOS平台的话,一般使用CocoaAsyncSocket(OC封装的框架):
·支持Mac/iOS平台;
·异步网络通讯库;
·支持TCP和UDP协议;
·支持IPv4和IPv6协议;
·全部采用代理(委托)设计模式;
·TCPServer自动接受连接;
·分别有两种封装:
1)GCD封装:GCDAsyncSocket/GCDAsyncUdpSocket;
2)Runloop封装:AsyncSocket/AsyncUdpSocket;
我们只需学习GCD的封装即可,毕竟更习惯;
通过Cocoapods装一下CocoaAsyncSocket;(GCD封装的,Runloop的版本已经被remove掉了)
使用CocoaAsyncSocket 进行TCP编程
TCP协议是流式(stream)协议,需要建立连接(三次握手),断开连接(四次握手),但没有所谓的黏包概念;
正式因为流式传输,发送端发送100K字节,接收端可能一次就读完,也可能分四次才能读完;但是接收端的接受顺序和发送端的发送顺序是一致的;
TCP协议是可靠传输,发送端的发出数据内容和顺序都会正确的到达接受端;
UDP协议是数据报(datagram)协议,不需要建立连接,数据报是不可靠传输;体现在:
1)发送方无法确认接收方是否成功接收;
2)接收方无法确认收到的数据报在发送端的顺序;
目标:iOS TCP/UDP Socket编程
1.基于tcp协议的聊天协议(自定义):
1)line-based;(基于行为单位的语义)
2)block-based;(基于块为单位的语义)
TCP链接的三次握手和四次挥手:
TCP连接三次握手、断开连接的四次握手:
建立起一个TCP连接需要经过“三次握手”:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
三次握手(Three-way Handshake)即建立一个TCP连接时,需要客户端和服务器总共发送3个包。三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换TCP 窗口大小信息。在socket编程中,客户端执行connect()时,将触发三次握手。
TCP连接的拆除需要发送四个包,因此称为四次握手(four-way handshake)。在socket编程中,任何一方执行close()操作即可产生握手(有地方称为“挥手”)操作。
之所以有“三次握手”和“四次握手”的区别,是因为连接时当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
我们使用wireShark验证一下上述过程:
我们会用到一个工具:netcat(在终端下通常是nc)是一个Unix工具,可以用于创建TCP(以及UDP)链接并进行监听;
1)我在自己的电脑上建立侦听;nc -t -l 8888 (-t:tcp,-l:listen ,8888:侦听的端口号)
2)我用同事的电脑进行连接;nc -t 172.16.200.91 8888 (我的电脑的ip地址,她的是172.16.201.13)
如下图所示:我们看到了3次握手和四次握手;(图1)
我们从这些抓包信息中还能看到一些信息:
IPv4:Internet Protocol Version 4;
Src:172.16.201.13;
Dst:172.16.200.91;
TCP:Transmission Control Protocol;
Src Port:58364;
Dst Port:8888;
过滤条件如:ip.addr == 172.16.201.13 tcp.port = 8888 等;
Socket连接建立之后的简单信息传输:
前面已经知道了如何建立连接;之后我们可以基于建立的连接进行信息传输:
(图2)
(图3)
(图4)
还可以进行图片传输:
server:nc -t -l 8888 > my-in.JPG(重定向输出:将接收数据重定向到指定文件);
client:cat my.JPG | nc -t 127.0.0.1 8888;(管道)
或client:nc -t 127.0.0.1 8888 < my.JPG;(重定向输入)
可以练习下;
总结