一TCP、UDP区别
1共同点:传输层协议,传输信息
2区别:
TCP传输控制协议 | UDP用户数据包协议 | |
可靠性 | 可靠 | 不可靠 |
连接性 | 面向连接 | 无连接 |
报文 | 面向字节流 | 面向报文 |
效率 | 传输效率低 | 传输效率高 |
双工性 | 全双工 | 一对一、一对多、多对一、多对多 |
流量控制 | 有(滑动窗口) | 无 |
拥塞控制 | 有(慢开始、拥塞避免、快重传、快恢复) | 无 |
tcp:面向连接,可靠,开销小,传输效率低,延时高,端到端传输
udp:无连接,不可靠,开销大,传输效率高,一对一、一对多、多对一、多对多
tcp有拥塞控制,可以慢开始,拥塞避免,快重传,快恢复。udp没有拥塞控制,不管网络是否拥塞,udp客户端都可以一直发送。
tcp适用于数据传输准确度要求度高的场合(电话)。udp适用于数据传输量大,实时性要求高的场合(短信)
tcp数据完整,按时,按序到达。udp尽最大努力交付数据。
二tcp如何保证可靠传输
TCP通过序列号、数据包检验、确认应答机制、超时重发、连接管理、流量控制、拥塞控制实现可靠性。
-
校验和: TCP在发送报文之前,发送方要计算校验和,收到数据后,接收方也要计算校验和,如果校验和不相等则丢弃。
-
序列号与确认应答:
-
序列号:TCP传输时将每个字节的数据都进行了编号,这就是序列号。
-
确认应答:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
-
序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。这也是TCP传输可靠性的保证之一。
-
-
超时重传: 在TCP传输过程中,我们在发送一部分数据后,都会等待对方的ACK确认报文,如果中间出现差错,没有收到ACK报文,这时候需要启动超时重传机制。这种超时重传机制保证了TCP在网络延迟或者报文丢失下的可靠传输。
超时的原因:
-
接收方没有收到TCP报文段:网络延迟或者丢包;
-
发送方没有收到ACK报文段:网络延迟或者ACK报文丢失。
-
-
连接管理:连接管理就是三次握手与四次挥手的过程(后面有详细解释)
-
流量控制: 流量控制的目的是让接收方来得及接收数据。这样避免了数据丢包以及网络拥塞等情况。
-
拥塞控制: 拥塞控制就是防止过多的数据注入到网络中,这样使网络中的路由器或者链路不至于过载。
-
三拥塞控制详解
-
TCP通过慢启动、拥塞避免、快重传以及快恢复这四个算法来进行拥塞控制
-
慢启动:一开始先设置一个比较小的拥塞窗口值cwnd(报文段的倍数),然后进行数据传输,每收到一个报文段的确认,我们就将cwnd+1,这样下来,cwnd总体上是乘以2^n的倍数增长。(慢启动非增长速度慢,只是增长的初始基数比较小)
-
拥塞避免: 因为慢启动算法的增长比较快,当cwnd = ssthresh(预先设置好的门限值)时,我们启动拥塞避免算法,窗口值开始线性增长。
随着拥塞避免算法的进行,网络出现超时的情况(这时判断为拥塞出现)。这时将cwnd降为一开始的值,重新进行慢开始-拥塞避免,并且此时的门限值设为出现拥塞时的cwnd的一半。
-
快重传: 快重传的目的是为了让发送方尽早知道某个报文段的丢失。如何知道呢?当我们重复收到某一个报文段的3次确认时,我们就可以判断,它的下一个报文段可能出现了丢失。这时我们启动快重传算法,立即重传丢失的报文段。
-
快恢复: 上面快重传算法的启动只是因为个别报文段的丢失,我们这时并不判断为网络拥塞,而是启动快恢复算法。我们将cwnd=ssthresh=当前cwnd的一半,并且开始拥塞避免算法。
当然,也有的快恢复算法是将当前拥塞窗口再增大3个报文段的值,因为既然收到了3个重复的ACK,则说明有三个分组已经离开了网络,不在占用网络资源而是停留在对方缓存当中,可以适当将窗口值增大。
-
四udp的问题
丢包与无序
如何解决丢包问题: 增大接收端的缓冲区大小; 增大接收端的处理能力;
无序:修改协议,增加超时重发,确认机制
五三次握手
所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。
1.TCP 为什么三次握手而不是两次握手
1.防止已失效的连接请求又传送到服务器端,因而产生错误。不太准确
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:
图1 TCP三次握手
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1(确认号码),随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
① SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产生时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
六 四次挥手
所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:
图2 TCP四次挥手
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:
图3 同时挥手
①第四次挥手客服端为什么要等待2MSL时间?
去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。
这恰恰就是2MSL( Maximum Segment Life)。
1. 当客户端的最后一次确认连接的报文丢失(第四次挥手发送的),服务器会再次发送FIN报文,等待客户端的确认,客户端在2MSL内收到服务器重传的报文再次确认(防止此时客户端已经关闭)。安心的释放tcp占用的资源,端口
2. 2MSL 的时间可以使所有已失效的报文都消失(防止对新建的连接造成影响),若在不等待2MSL,马上建立新的连接,那么在关闭连接前发送的失效报文段很可能影响本次连接。如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突
然而,TCP规定处于2MSL状态的的插口对(客户端IP/端口和服务器IP和端口)不能再次被使用。 若在2MSL状态下新建连接可以使用这个插口对,当已失效的数据包再次到达这个新连接,判断插口一致就会接受这个报文,但实际上这不是新建连接交互的数据,因此不能使用此无效数据包。
七 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
八
TCP传输信息
客户端:①创建socket,链接远端地址
②连接后发送数据和接收数据
③传输完毕,关闭socket
import socket
s=socket.socket()
host=10.0.0.0
port=123
s.connect(host,port)
print(s.recv(1024)
s.close()
服务端:①创建socket,绑定socket到本地ip与端口
②开始监听连接
③进入循环不断接收客户端的连接请求
④接受传来的数据,发送数据给对方
⑤传输完毕,关闭socket
import socket
s=socket.socket()
host=10.0.0.0
port=123
s.bind(host,port)
s.listen(5)
while True:
c,addr=s.accept()
print(addr)
c.send("hello")
c.close()
UDP传输信息
客户端:①创建socket,链接远端地址
②发送数据和接收数据
③传输完毕,关闭socket
import socket
s=socket.socket()
host=10.0.0.0
port=123
addr=(host,port)
data=input("the data you need to send:")
s.sendto(data.encode(),addr)
s.close()
服务端:①创建socket,绑定socket到本地ip与端口
②开始监听连接
③进入循环不断接收客户端的连接请求
④等待对方发送数据,接受传来的数据,
⑤传输完毕,关闭socket
import socket
s=socket.socket()
host=10.0.0.0
port=123
s.bind(host,port)
data=input("data:")
s.sendto(data.encode(),addr)
receive_data=s.recvfrom(1024)
s.close()