1.3 长链接与短链接服务器
UDP通讯模型中,在通讯开始以前,不须要创建相关的连接,只须要发送数据便可,相似于生活中的“写信”。并发
TCP通讯模型中,在通讯开始以前,必定要先创建相关的连接,才能发送数据,相似于生活中的"打电话"。socket
TCP在真正的读写操做以前,server与client之间必须创建一个链接,当读写操做完成后,双方再也不须要这个链接时它们能够释放这个链接。tcp
链接的创建经过三次握手,释放则须要四次握手,因此说每一个链接的创建都是须要资源消耗和时间消耗的。网站
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态。当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间。等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后能够再发一个ACK应答包。spa
在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当链接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。设计
不过在实际应用中能够经过设置 SO_REUSEADDR选项达到没必要等待2MSL时间结束再使用此端口。3d
创建链接——数据传输——关闭链接 ... 创建链接——数据传输——关闭链接
创建链接——数据传输 ...(保持链接)... 数据传输——关闭链接
长链接能够省去较多的TCP创建和关闭的操做,减小浪费,节约时间。对于频繁请求资源的客户来讲,较适用长链接。
client与server之间的链接若是一直不关闭的话,会存在一个问题,随着客户端链接愈来愈多,server迟早有扛不住的时候,这时候server端须要采起一些策略,如关闭一些长时间没有读写事件发生的链接,这样能够避免一些恶意链接致使server端服务受损;若是条件再容许就能够以客户端机器为颗粒度,限制每一个客户端的最大长链接数,这样能够彻底避免某个蛋疼的客户端连累后端服务。
长链接多用于操做频繁,点对点的通信,并且链接数不能太多状况。每一个TCP链接都须要三次握手,这须要时间,若是每一个操做都是先链接,再操做的话那么处理速度会下降不少,因此每一个操做完后都不断开,再次处理时直接发送数据包就OK了,不用创建TCP链接。例如:数据库的链接用长链接,若是用短链接频繁的通讯会形成socket错误,并且频繁的socket建立也是对资源的浪费。
而像WEB网站的http服务通常都用短连接,由于长链接对于服务端来讲会耗费必定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的链接用短链接会更省一些资源,若是用长链接,并且同时有成千上万的用户,若是每一个用户都占用一个链接的话,那可想而知。因此短链接多用于并发量大,但每一个用户无需频繁操做的状况。
在生活中,若是想让别人能更够打通我们的电话获取相应服务的话,须要作如下几件事情:
如同上面的电话机过程同样,在程序中,若是想要完成一个tcp服务器的功能,须要的流程以下:
示例:
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客户端要比服务器端简单不少。若是说服务器端是须要本身买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只须要找一个电话亭,拿起电话拨打便可,流程要少不少。
示例:
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()
运行效果:
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()