安卓开发网络编程学习之Socket

我们在做app网络流量分析的第一步就是先抓取流量数据包,抓包一般又分为以下两种情形:

  • 应用层抓包:Http(s)协议抓包,常用抓包工具有Charles、Burpsuite、Wireshark。
  • 会话层抓包:Socket端口通信抓包,用Socket实现TCP/UDP通信。常用抓包工具有Wireshark。

也就是app通信可以走Http(s)或者Socket,这里主要学习Socket通信,也分为两种方式:基于TCP和基于UDP的Socket通信。在学习Socket通信之前先复习一下计网中的一些概念。

OSI七层模型

按照OSI参考模型来划分的话,网络协议被划分为七层,从下到上分别为:

  • 物理层:单位为bit,利用传输介质为数据链路层提供物理连接,实现原始比特流的传输,常见设备为网卡,网线,集线器,中继器等。
    按照课本上的比喻,物理层就像是快递寄送过程中的交通工具,例如运输车。
  • 数据链路层:单位为帧,通过差错控制、流量控制等各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链
    路。数据链路层又分为MAC(媒体访问控制子层)层与LLC层(逻辑链路控制子层),MAC子层负责处理数据出错校验、成帧等,
    LLC层负责建立和维护两台通信设备之间的逻辑通信链路。常见设备为交换机。
  • 网络层:单位为数据包,负责点到点(更多的是指路由到路由)的传输在通信子网中进行路由选择和通信控制。具体来说,数据链路层
    的数据在这一层被转换为数据包,然后通过路径选择、分段组合、进/出路由等控制,将信息从一个网络设备传送到另一个网
    络设备。常见设备为路由器。
    这一层就是我们经常说的IP协议层,数据链路层中使用的物理地址(如MAC地址)仅解决网络内部的寻址问题。在不同子网之
    间通信时,为了识别和找到网络中的设备,每一子网中的设备都会被分配一个唯一的IP地址。IP地址就好比我们买东西快递时
    的集散中心,MAC地址就好比是说我们本人。
  • 传输层:单位为数据段。传输层建立了主机端到端(也就是源主机到目标主机)的链接,为上层协议提供端到端的可靠和透明的数据传输
    服务,包括处理差错控制和流量控制等问题。常见的传输层协议包括TCP传输控制协议与UDP用户数据报协议。
    TCP协议是一个面向连接的、可靠的协议。它处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发送方
    发送的大量数据。UDP协议是一个不可靠的、无连接协议,主要适用于不需要对报文进行排序和流量控制的场合。
  • 会话层:会话层就是负责建立、管理和终止表示层实体之间的通信会话。
  • 表示层:处理用户信息的表示问题,如编码、 数据格式转换和加密解密,压缩解压缩等。
  • 应用层:为用户的应用程序提供网络服务,常见应用层的网络协议有:HTTP,HTTPS,FTP(文件传输),Telnet(远程登录),POP3、
    SMTP(邮件)等。

TCP/IP四层模型

上面介绍的ISO制定的OSI参考模型的过于庞大和复杂。与此对照,由技术人员自己开发的TCP/IP协议栈获得了更为广泛的应用。贴上网上常见的一幅对照图:
在这里插入图片描述

  • 主机到网络层:提供给其上层网络互连层一个访问接口,以便在其上传递IP分组。
  • 网络互连层:负责点到点的传输(这里的"点"指主机或路由器)。主要定义了IP地址格式,使得不同应用类型的数据在Internet上传输。
    提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如IP协议。
  • 传输层:提供端到端的传输(这里的"端"指源主机到目标主机) 。在此层中,提供了节点间的数据传送服务,如传输控制协议(TCP)、
    用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据
    已被送达并接收。
  • 应用层:应用程序间沟通的层,它面向不同的网络应用引入了不同的应用层协议。如简单电子邮件传输(SMTP)、文件传输协议
    (FTP)等。

TCP/UDP协议、IP协议

这里了解下这三种协议,不是学习的重点就不详细分析了,借鉴下psychosis博文:TCP/UDP是传输层的协议,上面已经大致提到概念。IP是网络层协议,它提供不可靠、无连接的服务,TCP、UDP数据被封装在IP数据报中传送。

**TCP(传输控制协议)**是一种可靠的、面向连接的传输层协议。即在收发数据前 ,都需要与对面主机建立可靠的连接,通常使用我们常说的TCP建立连接的三次握手、TCP连接释放时的四次挥手保证可靠性。TCP协议头部如下图所示:
在这里插入图片描述
●源、目标端口号字段:占16比特。TCP协议通过使用"端口"来标识源端和目标端的应用进程。端口号可以使用0到65535之间的任何数字。在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号。
  ●顺序号字段:占32比特。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。  
  ●确认号字段:占32比特。只有ACK标志为1时,确认号字段才有效。它包含目标端所期望收到源端的下一个数据字节。  
  ●头部长度字段:占4比特。给出头部占32比特的数目。没有任何选项字段的TCP头部长度为20字节;最多可以有60字节的TCP头部。  
  ●标志位字段(U、A、P、R、S、F):占6比特。之后TCP三次握手与四次挥手会用到这里的标志位,各比特的含义如下:  
  ◆URG:紧急指针(urgent pointer)有效。  
  ◆ACK:确认序号有效。  
  ◆PSH:接收方应该尽快将这个报文段交给应用层。  
  ◆RST:重建连接。  
  ◆SYN:发起一个连接。  
  ◆FIN:释放一个连接。  
  ●窗口大小字段:占16比特。此字段用来进行流量控制。单位为字节数,这个值是本机期望一次接收的字节数。  
  ●TCP校验和字段:占16比特。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。  
  ●紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。  
  ●可选项字段:占32比特。可能包括"窗口扩大因子"、"时间戳"等选项。

UDP(用户数据报)协议是一个不可靠的、无连接协议,即源主机在传送数据前不需要和目标主机建立连接。UDP协议头部如下图所示:
在这里插入图片描述
●源、目标端口号字段:占16比特。作用与TCP数据段中的端口号字段相同,用来标识源端和目标端的应用进程。  
  ●长度字段:占16比特。标明UDP头部和UDP数据的总长度字节。  
  ●校验和字段:占16比特。用来对UDP头部和UDP数据进行校验。和TCP不同的是,对UDP来说,此字段是可选项,而TCP数据段中的校验和字段是必须有的。

IP协议是TCP/IP协议族中最为核心的协议,它提供不可靠、无连接的服务。IP协议头部如下图所示:
 在这里插入图片描述
  ●版本(Version)字段:占4比特。用来表明IP协议实现的版本号,当前一般为IPv4,即0100。  
  ●报头长度(Internet Header Length,IHL)字段:占4比特。是头部占32比特的数字,包括可选项。普通IP数据报(没有任何选项),该字段的值是5,即160比特=20字节。此字段最大值为60字节。  
  ●服务类型(Type of Service ,TOS)字段:占8比特。其中前3比特为优先权子字段(现已被忽略)。第8比特保留未用。第4至第7比特分别代表延迟、吞吐量、可靠性和花费。当它们取值为1时分别代表要求最小时延、最大吞吐量、最高可靠性和最小费用。这4比特的服务类型中只能置其中1比特为1。可以全为0,若全为0则表示一般服务。服务类型字段声明了数据报被网络系统传输时可以被怎样处理。例如:TELNET协议可能要求有最小的延迟,FTP协议(数据)可能要求有最大吞吐量,SNMP协议可能要求有最高可靠性,NNTP(Network News Transfer Protocol,网络新闻传输协议)可能要求最小费用,而ICMP协议可能无特殊要求(4比特全为0)。实际上,大部分主机会忽略这个字段,但一些动态路由协议如OSPF(Open Shortest Path First Protocol)、IS-IS(Intermediate System to Intermediate System Protocol)可以根据这些字段的值进行路由决策。  
  ●总长度字段:占16比特。指明整个数据报的长度(以字节为单位)。最大长度为65535字节。  
  ●标志字段:占16比特。用来唯一地标识主机发送的每一份数据报。通常每发一份报文,它的值会加1。  
  ●标志位字段:占3比特。标志一份数据报是否要求分段。  
  ●段偏移字段:占13比特。如果一份数据报要求分段的话,此字段指明该段偏移距原始数据报开始的位置。  
  ●生存期(TTL:Time to Live)字段:占8比特。用来设置数据报最多可以经过的路由器数。由发送数据的源主机设置,通常为32、64、128等。每经过一个路由器,其值减1,直到0时该数据报被丢弃。  
  ●协议字段:占8比特。指明IP层所封装的上层协议类型,如ICMP(1)、IGMP(2) 、TCP(6)、UDP(17)等。  
  ●头部校验和字段:占16比特。内容是根据IP头部计算得到的校验和码。计算方法是:对头部中每个16比特进行二进制反码求和。(和ICMP、IGMP、TCP、UDP不同,IP不对头部后的数据进行校验)。  
  ●源IP地址、目标IP地址字段:各占32比特。用来标明发送IP数据报文的源主机地址和接收IP报文的目标主机地址。  
  可选项字段:占32比特。用来定义一些任选项:如记录路径、时间戳等。这些选项很少被使用,同时并不是所有主机和路由器都支持这些选项。可选项字段的长度必须是32比特的整数倍,如果不足,必须填充0以达到此长度要求。
  需要用的时候,就对比相应的格式即可。

TCP三次握手、四次挥手

 三次握手用于连接建立,四次挥手用于连接释放。

三次握手过程图:
在这里插入图片描述
再详细解释下各个标志字段的含义:

  • seq:用来标记数据段的顺序。TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号,序列号seq就是这个报文段中的第一个字节的数据编号。
  • SYN:连接建立时用于同步序号,连接建立后SYN被置为0。当SYN=1,ACK=0时表示这是一个连接请求报文段,若同意连接,则在响应报文段中使得SYN=1,ACK=1。
  • ack:期待收到对方下一个报文段的第一个数据字节的序号,数值上等于当前报文段最后一个字节的编号+1。
  • ACK:仅当ACK=1时,确认号字段ack才有效。
  • 终止FIN:用来释放一个连接,FIN=1表示希望断开连接。

然后看握手的过程:

  • 第一次握手:client发送SYN = 1,seq = J(随机)到server,表示希望建立连接。随后client进入SYN_SENT状态,等待服务器确认。
  • 第二次握手:server收到建立连接请求,同意连接,并且使SYN=1,ACK=1,ack=J+1,并且自己也随机生成一个seq = K,将这个数据包发送给client以确认连接请求,Server进入SYN_RCVD状态。
  • 第三次握手:客户端收到服务器的SYN+ACK包,知道server同意连接请求,便发送ACK = 1,ack = K+1,seq = J+1,并将该数据包发送给server,server检查ack是否为K+1,ACK是否为1,如果正确则代表连接建立成功,Client和Server就都进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

四次挥手:
断开TCP连接时,需要client和server总共发送4个包以确认连接的断开。 在socket编程中,这一过程由client或server任一方执行close来触发。这里假设主动方为client,被动方为server。
在这里插入图片描述

  • 第一次挥手:client主动发出连接释放报文随即停止发送数据。连接释放报文首部标志字段FIN=1,序列号seq=u。同时client进入FIN-WAIT-1(终止等待1)状态。
  • 第二次挥手:server收到连接释放报文,发出确认报文,ACK=1,ack=u+1,随机序列号seq=v,此时server进入了CLOSE-WAIT(关闭等待)状态。
  • 第三次挥手:client收到server的确认请求后,client进入FIN-WAIT-2(终止等待2)状态,并接收server发送完最后所有的数据后,等待服务器发送连接释放报文。server发送完所有的报文后,就向client发送连接释放报文,FIN=1,ACK=1,ack=u+1,由于server上面还发送了一些数据,假定此时seq=w,发送完连接释放报文后,server就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  • 第四次挥手:client接收到server发送的连接释放报文后,也要发送确认报文,ACK=1,ack=w+1,seq=u+1,发送完确认后,client进入TIME-WAIT(时间等待)状态,经过2∗MSL(最长报文段寿命)的时间后,当client撤销相应的TCB(传输控制块)后,进入CLOSED状态。server收到client发出的确认后,会立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看出,server结束TCP连接的时间要比client早一些。

看一些常见的Q&A来加深理解,参考青柚_ 博文

  • Q:为什么连接的时候是三次握手,关闭的时候却是四次挥手?

  • A:连接请求的时候,server收到client发送的请求连接报文后,可以直接发送同意请求的应答报文,但是关闭连接的时候,server在收到client发送的连接释放报文后,不能马上发送同意连接释放的报文,而是先发送一个确认报文确认server收到了这个请求,然后再等待server所有的数据发送完成后,才能发送FIN=1同意连接释放报文。所以需要四次挥手。

  • Q:两次握手进行连接可以吗?

  • A:不可以,三次握手完成了两个重要的功能:双方做好发送数据的准备工作以及双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
    只有两次握手来建立连接的话,死锁是可能发生的。举个例子:client与server通信,client向server发送连接建立请求,这是第一次握手,server收到了这个分组,并发送了确认应答分组,如果按照两次握手的规定,到这儿server已经认为连接已经建立了,已经可以向client发送数据了。乍一看好像也可以建立连接,但是如果这个第二次握手的回应报文在发送的过程中丢失的话,client就不知道server是否已经准备好建立连接,不知道server建立什么序列号,甚至怀疑server是否收到自己的连接请求。在这种情况下,client认为连接还未建立成功,将忽略server发来的任何数据分组,只等待连接确认应答分组,然而这个分组却早已丢失。server在发出的分组超时无应答后,重复发送同样的分组。这样就形成了死锁。

  • Q:为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

  • A:和两次握手原因相似,client必须要确认server是否收到最后的ACK报文。最后一次握手ACK可能丢失(ACK报文是用来应答的),server如果没有收到ACK,将不断重复发送FIN。所以client不能立即关闭,它必须确认server接收到了该ACK。client会在发送出ACK之后进入到TIME_WAIT状态。client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,说明ACK丢失,那么client就会重发ACK并再次等待2MSL。2MSL指的是是两倍的MSL(Maximum Segment Lifetime),MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需要的最大时间。如果直到2*MSL,client都没有再次收到FIN,那么client判断ACK已经被成功接收,才断开TCP连接。

  • Q:如果已经建立了连接,但是客户端突然出现故障?

  • A:TCP还设有一个保活计时器。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

socket简介

在写 Demo之前首先看下socket百度百科简介:

socket(套接字)就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。应用程序可以通过Socket向网络发送请求或者应答网络请求。socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必须的五种信息:连接所使用的协议;本地主机的IP地址;本地远程的协议端口;远地主机的IP地址以及远地进程的协议端口。

简单点说与http一样都是用来通信的。与http有什么不同呢?之后解释。先看下socket的通信过程:

socket通信模型如下:
在这里插入图片描述
socket通信大致步骤如下:

server端:

  • 1):创建ServerSocket对象并绑定监听的端口
  • 2):调用accept()方法阻塞,监听客户端的请求
  • 3):连接建立后,通过输入流读取客户端发送的请求信息,通过输出流向客户端发送响应信息
  • 4):关闭socket与相关资源

client端

  • 1):创建Socket对象,指明需要连接的服务器地址和端号
  • 2):连接建立后,通过输出流向服务器发送请求信息,通过输入流获取服务器响应的信息
  • 3):关闭socket与相关资源

我们看到socket和http一样也是用来进行网络请求与回应的,那么什么时候使用socket,什么时候使用http呢?两者之间的主要区别是什么?

  • http:http是短连接,客户端使用http协议进行请求,发送请求的时候需要封装http请求头,并绑定请求的数据,服务器一般有web服务器配合,http的请求方式为客户端主动发起请求,服务器才能给予响应,一次请求完毕后就断开连接以节省资源。且服务器不能主动跟客户端发起响应。当然HTTP 1.1版本时,已经增加了持久连接支持,但还是无状态的,或者说是不可以信任的。

  • socket:socket是长连接,客户端跟服务器直接使用socket套接字进行连接,在一次连接请求后不会断开,所以客户端和服务器可保持连接通道,双方都可以主动发送数据。

  • 一般常见的实时波动的股票这种软件或者QQ这种及时通讯的软件通信会使用scoket,因为频繁的建立连接肯定不能保证实时性。而web网站这种会使用http,因为如果每个用户都占用一个长连接的话,会占用很多资源。

Demo

这里整了个Java版本的… ,Android的整了一天也没调试通,这个之后有时间再研究研究。
server端运行后,如下图所示:
在这里插入图片描述
client端运行后,发现数据已经发送到server端:
在这里插入图片描述
Android的代码之后看懂了会再写一下。

猜你喜欢

转载自blog.csdn.net/weixin_42011443/article/details/106978255