首页 > 其他 > 详细

TCP

时间:2020-03-21 00:26:01      阅读:246      评论:0      收藏:0      [点我收藏+]

1. TCP介绍

1.1 UDP与TCP在通行模型上的区别

1.2 三次握手与四次挥手

1.3 长连接与短连接

2. TCP基础代码示例

 

 

1. TCP 介绍

1.1 UDP与TCP在通行模型上的区别

UDP通信模型

UDP通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中的“写信”。

技术分享图片

TCP通信模型

TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中的"打电话"。

技术分享图片

 

1.2 三次握手与四次挥手

TCP在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接。

连接的建立通过三次握手,释放则需要四次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。

技术分享图片

TCP的10种状态:

技术分享图片

 注意:

  • 当一端收到一个FIN,内核让read返回0来通知应用层另一端已经终止了向本端的数据传送。
  • 发送FIN通常是应用层对socket进行关闭的结果。

TCP的2MSL问题:

 技术分享图片

说明:

2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态。当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间。等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。

在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。

不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。

 

1.3 TCP长连接与短连接

短连接的操作步骤:

建立连接——数据传输——关闭连接 ... 建立连接——数据传输——关闭连接

技术分享图片

长连接的操作步骤:

建立连接——数据传输 ...(保持连接)... 数据传输——关闭连接

技术分享图片

TCP长/短连接的优点和缺点:

  • 长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。

  • client与server之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。

  • 短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

TCP长/短连接的应用场景:

  • 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,再次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,而且频繁的socket创建也是对资源的浪费。

  • 而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知。所以短连接多用于并发量大,但每个用户无需频繁操作的情况

 

2. TCP基础代码示例

TCP服务器

在生活中,如果想让别人能更够打通咱们的电话获取相应服务的话,需要做以下几件事情:

  1. 买个手机
  2. 插上手机卡
  3. 设计手机为正常接听状态(即能够响铃)
  4. 静静的等着别人拨打

如同上面的电话机过程一样,在程序中,如果想要完成一个tcp服务器的功能,需要的流程如下:

  1. socket创建一个套接字
  2. bind绑定ip和port
  3. listen使套接字变为可以被动连接
  4. accept等待客户端的链接
  5. recv/send接收发送数据

示例:

 1 import socket
 2 
 3 # 创建socket
 4 tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 5 
 6 # 绑定本地信息
 7 address = ("", 7788)
 8 tcpSocket.bind(address)
 9 
10 # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的连接的
11 tcpSocket.listen(5)
12 
13 # 当有新的客户端来连接服务器时,就会产生一个新的套接字来专门为这个客户端服务
14 # newSocket就是用来为这个新来的客户端服务的
15 # tcpSocket就可以省下来专门等待其他新客户端的连接
16 newSocket, clientAddr = tcpSocket.accept()
17 
18 # 接收对方发送过来的数据,最大接收1024个字节
19 recvData = newSocket.recv(1024)
20 print("接收到数据为:", recvData.decode())
21 
22 # 发送一些数据给客户端
23 newSocket.send("Thank  you!".encode())
24 
25 # 关闭为这个客户端服务的套接字。只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
26 newSocket.close()
27 
28 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
29 tcpSocket.close()

运行效果:

技术分享图片

TCP客户端

TCP客户端要比服务器端简单很多。如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多。

示例:

 1 import socket
 2 
 3 # 创建socket
 4 tcpClientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 5 
 6 # 连接服务器端
 7 serAddress = ("192.168.3.4", 7788)
 8 tcpClientSocket.connect(serAddress)
 9 
10 # 提示用户输入数据
11 sendData = input("请输入要发送给服务器的数据:")
12 
13 tcpClientSocket.send(sendData.encode())
14 
15 # 接收对方发送过来的数据,最大接收1024个字节
16 recvData = tcpClientSocket.recv(1024)
17 print("接收到数据为:", recvData.decode())
18 
19 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
20 tcpClientSocket.close()

运行效果:

技术分享图片

应用:1V1聊天

服务器端

 1 import socket
 2 
 3 # 创建socket
 4 tcpServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 5 
 6 # 绑定本地信息
 7 address = ("", 7788)
 8 tcpServerSocket.bind(address)
 9 
10 # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的连接了
11 tcpServerSocket.listen(5)
12 
13 while True:
14 
15     # 如果有新的客户端来连接服务器,那么就产生一个新的套接字专门为这个客户端服务器
16     # newSocket用来为这个客户端服务
17     # tcpSerSocket就可以省下来专门等待其他新客户端的连接
18     newSocket, clientAddr = tcpServerSocket.accept()
19     
20     while True:
21     
22         # 接收对方发送过来的数据,最大接收大小为2014字节
23         recvData = newSocket.recv(1024).decode()
24         
25         # 如果对方发送“88”,则意味着客户端关闭连接
26         if recvData == "88":
27             print("Receive:", recvData)
28             print("----关闭聊天----")
29             break
30         else:
31             print("Receive:", recvData)
32             sendData = input("Send:")
33             newSocket.send(sendData.encode())
34             
35     # 关闭这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了
36     # 如果还需要服务,只能再次重新连接
37     newSocket.close()
38 
39 # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
40 tcpSerSocket.close()    

客户端

 1 import socket
 2 
 3 # 创建socket
 4 tcpClientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 5 
 6 # 连接服务器端
 7 serAddress = ("localhost", 7788)
 8 tcpClientSocket.connect(serAddress)
 9 
10 while True:
11 
12     # 提示用户输入数据
13     sendData = input("Send:")
14     
15     tcpClientSocket.send(sendData.encode())
16     if sendData == "88":
17         break
18     else:
19         # 接收对方发送过来的数据,最大接收1024个字节
20         recvData = tcpClientSocket.recv(1024)
21         print("Receive:", recvData.decode())
22 
23 # 关闭监听套接字。只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
24 tcpClientSocket.close()

运行效果

技术分享图片

 

TCP

原文:https://www.cnblogs.com/juno3550/p/12521478.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!