TCP 协议和 UDP 协议?
-
TCP(transfer control protocol): 传输控制协议,提供面向连接(TCP 的三次握手四次分手)、可靠的字节流服务;
-
UDP(user data protocol): 用户数据报协议,是一个简单的面向数据报的,无连接的运输层协议。
TCP 与 UDP 区别总结:
-
TCP面向连接(如打电话要先拨号建立连接)
UDP是无连接的,即发送数据之前不需要建立连接 -
TCP 的传输是以数据流模式
UDP 的传输是以数据报模式 -
TCP 提供可靠的服务(通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达)
UDP 尽最大努力交付,即不保证可靠交付 -
UDP 具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信
-
每一条TCP连接只能是点到点的
UDP 支持一对一,一对多,多对一和多对多的交互通信 -
TCP 对系统资源要求较多
UDP 对系统资源要求较少
TCP 的三次握手过程:
三次握手示意图:
-
第一次握手:
建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
SYN:同步序列编号(Synchronize Sequence Numbers)。 -
第二次握手:
服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; -
第三次握手:
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据
为什么需要三次握手?
明明两次就可以建立连接的为什么还要加第三次的确认?
如果发送两次就可以建立连接话,那么只要客户端发送一个连接请求,服务端接收到并发送了确认,就会建立一个连接。
但是可能会出现问题:如果一个连接请求在网络中跑的慢,超时了,这时客户端会重发请求,但是这个跑的慢的请求最后还是跑到了,然后服务端就接收了两个连接请求,然后全部回应就会创建两个连接,导致浪费资源。
如果加了第三次客户端确认,客户端在接受到一个服务端连接确认请求后,后面再接收到的连接确认请求就可以抛弃不管了。
所以需要三次握手
TCP 的四次分手过程:
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送;
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1;
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A;
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
为什么分手需要四次?
因为 TCP 是双向的,所以需要在两个方向分别关闭,每个方向的关闭又需要请求和确认,所以一共需要4次。
什么是socket?
-
socket(ip:port):Unix ----> (Linux,IOS) ;
一切皆文件 ----> socket也是一种特殊的文件;
打开文件(open) ----> 读写(read/write) ----> 关闭文件(close) -
socket模块:TCP/IP协议族的封装, 把应用层和TCP/IP协议族通信的中间软件抽象出来。
socket编程
scoket 编程的客户端 - 服务端工作方式示意图
基于 TCP 的通信
TCP 工作方式示意图:
服务端:
import socket
# 1. 创建一个socket对象
# family指定使用IP协议的版本 -> IPV4:AF_INET,IPV6:AF_INET6
# type指定传输层使用的协议类型 -> TCP(SOCK_STREAM),UDP(SOCK_DGRAM)
server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
# 2. 绑定一个IP和端口,供客户端来连接
server.bind(('172.25.254.67',5000))
# 3. 监听是否有客户端连接
server.listen(5)
print('服务端正在启动......')
# 4. 接收客户端的连接; accept() -> (socket object, address info)
clientSocket,clientAddress = server.accept()
print('客户端地址:',clientAddress)
# 5. 接收客户端发送的消息
recvData = clientSocket.recv(1024)
# 接收消息时,需要将消息decode解码为str类型
print('服务端接收的消息:',recvData.decode('utf-8'))
# 6. 给客户端回复消息; send()发送的消息需要encode编码成为bytes类型
clientSocket.send('你好,客户端!'.encode('utf-8'))
# 7.关闭socket对象
server.close()
clientSocket.close()
客户端:
import socket
# 1. 创建一个socket对象
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
# 2. 连接服务端
client.connect(('172.25.254.67',5000))
# 3. 给服务端回复消息
client.send('你好,服务端!'.encode('utf-8'))
# 4. 接收客户端发送的消息
recvData = client.recv(1024)
print('客户端接收的消息:',recvData.decode('utf-8'))
# 5.关闭socket对象
client.close()
基于 UDP 的通信
UDP 工作方式示意图:
服务端:
import socket
# 1. 实例化socket对象
udpServer = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# 2. 绑定IP和端口
# 0.0.0.0代表开放所有的IP地址
udpServer.bind(('0.0.0.0', 6000))
print('等待客户端UDP连接......')
# 3. 接收客户端的连接
recvData, address = udpServer.recvfrom(1024)
print('接收到客户端的数据:',recvData.decode('utf-8'))
# 4. 给客户端回复消息
udpServer.sendto(b'hello client',address)
# 5. 关闭socket对象
udpServer.close()
客户端:
import socket
udpClient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
udpClient.sendto(b'hello server', ('172.25.254.67', 6000))
recvData, address = udpClient.recvfrom(1024)
print('接收到服务端的数据:', recvData.decode('utf-8'))
udpClient.close()
启动服务端:
启动客户端,接收到服务端发送的消息,并给服务端发送一个消息:
返回服务端,接收到客户端发送的消息: