目录
TCP协议
传输层协议,用于应用程序端口的传输。
TCP 是面向有连接且可靠的通信传输协议,面向有连接是指在传送数据之前必须先建立连接,数据传送完成后要释放连接。并且 可实现“顺序控制”和“重发控制”的机制。
在TCP/IP协议簇中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的且通过四次挥手关闭连接。
三次握手:所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。
第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。
第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,且设置ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。
第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。(双方验证标志值)
四次挥手:四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
-
第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
-
第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
-
第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
-
第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ack则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL(大概4分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次挥手。(双方验证)
UDP协议
UDP 是面向报文的通信传输协议,所谓面向报文,指它的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。UDP 是不具有可靠性的数据报协议。在 UDP 的情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此,应用有时会根据自己的需要进行重发处理。
例如:发短信,不需要双方建立连接。
优劣:效率高,数据传输不安全,容易丢包
Socket概念
人们经常把socket翻译为套接字,socket其实就是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
其实socket就是一个Python中的一个模块。我们通过调用模块中已经实现的方法建立两个进程/应用之间的连接和通信。
基于TCP的socket通讯流程图:
服务端server
import socket
#1.创建一个套接字对象(区分类型)
sk = socket.socket(type=socket.SOCK_STREAM)
#2.绑定ip和端口
sk.bind(('192.168.21.253',8898))
#3.监听外部连接
sk.listen()
#4.处理/接收监听到的客户端连接
#conn表示建立好的连接通道抽象成的对象
#address就是接收到客户端的ip和port
conn,address = sk.accept()
#accept是一个阻塞方法,程序运行到此处就hang住,除非某种条件成立后,结束阻塞
#结束阻塞条件就是成功的接收到一个客户端连接
#5.基于建立好的连接通道进行数据的收发
recv_msg = conn.recv(1024)
print('服务端接受到的数据为:',recv_msg)
send_msg = input('请录入发送给客户端的数据:')
conn.send(send_msg.encode()) #会不会报错conn.close()
sk.close()
客户端client
import socket
#1.创建套接字对象
sk = socket.socket(type=socket.SOCK_STREAM)#2.连接目的服务器
sk.connect(('192.168.21.253',8898))#3.数据的收发
send_msg = input('请输入发送的数据:')
sk.send(send_msg.encode())recv_msg = sk.recv(1024)
print('接受到服务端的数据:',recv_msg)sk.close()
基于UDP的socket通讯流程:
服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束。
服务端server
import socket
#创建的是udp类型的套接字对象
sk = socket.socket(type=socket.SOCK_DGRAM)sk.bind(('127.0.0.1',8898))
#必须先接受客户端的数据,从接受的角度可以提取出客户端的地址
recv_data,addr = sk.recvfrom(1024)
recv_data = recv_data.decode()
print(recv_data,addr)
#根据指定地址将数据发送给目标应用
sk.sendto(b'nihao',addr)sk.close()
客户端client
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)host_addr = ('127.0.0.1',8898)
sk.sendto(b'ni hao server',host_addr)
recv_data,addr = sk.recvfrom(1024)
print(recv_data)sk.close()