TCP协议,传输控制协议,是一种面向连接的(通信双方必须先建立连接才能进行数据的传输)、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤.
TCP 通信模型中,在通信开始之前,一定要先建立相关连接,才能发送数据。
第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
完成了三次握手,客户端和服务器端就可以开始传送数据。
第一次挥手:A数据传输完毕需要断开连接,A的应用进程向其TCP发出连接释放报文段(FIN = 1,序号seq = u),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1状态,等待B的确认。
第二次挥手:B收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),B进入CLOSE-WAIT关闭等待状态,此时的TCP处于半关闭状态,A到B的连接释放。而A收到B的确认后,进入FIN-WAIT-2状态,等待B发出的连接释放报文段。
第三次挥手:当B数据传输完毕后,B发出连接释放报文段(FIN = 1,ACK = 1,序号seq = w,确认号ack=u+1),B进入LAST-ACK(最后确认)状态,等待A 的最后确认。
第四次挥手:A收到B的连接释放报文段后,对此发出确认报文段(ACK = 1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,A才进入CLOSE状态。
#TCP特点
当应用程序希望通过UDP与一个应用程序通信时,传输数据之前源端和终端不建立连接。当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。
import socket
#创建套接字
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立连接
tcp_client.connect(('127.0.0.1',8050))
data = input('输入信息:')
#发送数据
tcp_client.send(data.encode('utf-8'))
#接收数据
new_data = tcp_client.recv(1024)
print(new_data.decode('utf-8'))
#关闭套接字
tcp_client.close()
1.socket创建套接字
2.bind绑定IP和port
3.listen使套接字变为可以被动链接
4.accept等待客户端的链接
5.recv/send接收发送数据
import socket
#创建套接字
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立绑定
tcp_server_socket.bind(('127.0.0.1',8050))
#监听
tcp_server_socket.listen()
#等待客户端连接
new_server_socket,addr = tcp_server_socket.accept()
#接收数据
data = new_server_socket.recv(1024)
print(data.decode('utf-8'))
new_data=input('输入传送数据:')
#发送数据
new_server_socket.send(new_data.encode('utf-8'))
new_server_socket.close()
tcp_server_socket.close()
当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。
send发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡。
应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。
当发送网络数据时,tcp协议会根据Nagle算法将时间间隔短,数据量小的多个数据包打包成一个数据包,先发送到自己操作系统的缓存中,然后操作系统将数据包发送到目标程序所对应操作系统的缓存中,最后将目标程序从缓存中取出,而第一个数据包的长度,应用程序并不知道,所以会直接取出数据或者取出部分数据,留部分数据在缓存中,取出的数据可能第一个数据包和第二个数据包粘到一起。
服务端
import socket
def main():
#创建套接字
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立绑定
tcp_server_socket.bind(('127.0.0.1',8050))
#监听
tcp_server_socket.listen()
# 等待客户端连接
while True:
new_server_socket, addr = tcp_server_socket.accept()
#接收数据
while True:
g_data = new_server_socket.recv(1024)
print(g_data.decode('utf-8'))
if g_data == b'':
break
# 发送数据
# new_data=input('输入传送数据:')
# new_server_socket.send(new_data.encode('utf-8'))
new_server_socket.close()
tcp_server_socket.close()
if __name__ == '__main__':
main()
客户端
import socket
def main():
#创建套接字
tcp_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立连接
tcp_client.connect(('127.0.0.1',8050))
# data = input('输入信息:')
#分两次发送数据
tcp_client.send('data'.encode('utf-8'))
tcp_client.send('has'.encode('utf-8'))
#接收数据
# new_data = tcp_client.recv(1024)
# print(new_data.decode('utf-8'))
#关闭套接字
tcp_client.close()
if __name__ == '__main__':
main()
运行之后,我们在服务端接收到的信息为datahas。
这里我们需要引用struct模块,在客户端使用模块中的stuct.pack()方法,在服务端使用stuct.unpack()方法。客户端发送数据前,先对数据数据长度,根据数据长度来接收指定大小的数据。
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。
语法格式:struct.pack(fmt, v1, v2, …),参数fmt是格式字符串详细见下图,v1, v2, …表示要转换的python值。返回值是一个元组。
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。
语法格式:struct.unpack(fmt, string)
服务端
import socket,struct
def main():
#创建套接字
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立绑定
tcp_server_socket.bind(('127.0.0.1',10001))
#监听
tcp_server_socket.listen()
while True:
# 等待客户端连接
new_server_socket, addr = tcp_server_socket.accept()
while True:
#接收数据长度
data = new_server_socket.recv(4) #由于'i'格式占用四个字节,指定接收内容
# print(data)
#实际数据的长度
try:
num = struct.unpack('i',data)[0]
except:
break
if num < 1024: #当数据小于1024时
total_data = new_server_socket.recv(num).decode('utf-8')
print(total_data)
#根据数据长度接收数据
else:
l_data = 0
total_data = '' #接收结果
while l_data < num: #当数据大于1024
#接收数据
g_data = new_server_socket.recv(1024).decode('utf-8') #每次接收1024
total_data += g_data
l_data += len(g_data)
print(total_data)
new_server_socket.close()
tcp_server_socket.close()
if __name__ == '__main__':
main()
客户端
import socket,struct
def send_data(data):
msg = data # 数据内容
b_data = struct.pack('i', len(msg)) # 将数据转化为字节流
tcp_client.send(b_data)
tcp_client.send(msg.encode('utf-8'))
if __name__ == '__main__':
# 创建套接字
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
tcp_client.connect(('127.0.0.1',10001))
info = 'ad'
send_data(info)
info = 'qaq.123456'
send_data(info)
# 关闭套接字
tcp_client.close()