TCP(传输控制协议)和UDP(用户数据报协议)是两种常见的互联网传输协议,它们之间有很多区别,包括以下几个主要方面:
TCP是传输控制协议,是面向连接的通讯协议(如:打电话),通过三次握手建立连接,通讯完成时四次挥手,一般应用在对安全性、完整性有严格要求的场景,如FTP、SMTP、HTTP等
UDP是用户数据报协议,是面向无连接的通讯协议(如:发短信)
常用的有两种:
from socket import *
server = socket(AF_INET, SOCK_DGRAM)
server_host_port = ('127.0.0.1', 6000) # 服务器的IP地址和端口
# 接收数据前绑定端口
server.bind(server_host_port)
while True:
# 接收数据
data = server.recvfrom(1024)
# print('data:', data) # (b'\xe4\xbd\xa0\xe5\xa5\xbd', ('127.0.0.1', 61328))
print('访问者:', data[0].decode('utf-8')) # 你好
# print(f'客户端的IP:{data[1][0]} \n客户端的端口:{data[1][1]}')
"""重新发送数据"""
send_data = input('客服说:')
server.sendto(send_data.encode('utf-8'), data[1])
# server.close()
from socket import *
client = socket(AF_INET, SOCK_DGRAM)
server_host_port = ('127.0.0.1', 6000) # 指定数据接收方
while True:
data = input('访问者:')
data = data.encode('utf-8')
client.sendto(data, server_host_port) # 发送数据
if data.decode('utf-8') == 'bye':
break
"""接收返回数据数据"""
recv_data = client.recvfrom(1024)
print(f"客服说:{recv_data[0].decode('utf-8')}")
print('程序关闭')
client.close()
udp的交互使用:sendto 和 recvfrom
SOCK_DGRAM:表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。对应于无连接的 UDP服务应用。
from socket import *
import struct
server_socket = socket(AF_INET, SOCK_DGRAM)
host_port = ('localhost', 8888) # 端口号
# 开始监听
server_socket.bind(host_port)
# 接收数据
data = server_socket.recvfrom(1024)
print(data, type(data))
# 解析操作码
recv_data = data[0]
new_data = struct.unpack('!H', recv_data[:2])
print('客户端请求的操作码:', new_data)
# 解析文件名
file_name = recv_data[2:-7].decode('utf-8')
print('客户端请求下载的文件名:', file_name)
server_socket.close()
from socket import *
import struct
client_socket = socket(AF_INET, SOCK_DGRAM)
host_port = ('localhost', 8888) # 端口号
file_name = input('请输入需要上传的文件名:').encode('utf-8')
print('file_name:', file_name, len(file_name))
data = struct.pack('!H%dsb5sb' % len(file_name), 1, file_name, 0, 'octet'.encode('utf-8'), 0)
# 发送数据
client_socket.sendto(data, host_port)
client_socket.close()
tcp的交互使用:send 和 recv
SOCK_STREAM:表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。针对于面向连接的TCP服务应用;
from socket import *
import random
# 创建SOCKET对象
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定IP和端口
host_port = ('', 6666) # 不写本机所有
server_socket.bind(host_port)
# 设置listen
server_socket.listen(5)
while True:
# 等待客户端连接
client_socket, addr = server_socket.accept()
print('客户端已连接,3次握手完成! ')
# print('client_socket:', client_socket)
# 接收数据
data = client_socket.recv(1024).decode('utf8')
print('data:', data)
oper_code = data.split(':')[0] # 操作码
recv_data = data.split(':')[1] # 需要上传和下载的文件
if oper_code == '1': # 下载操作
file_read = open(recv_data, 'r', encoding='utf8')
data = file_read.read()
# 将数据发给客户端
client_socket.send(data.encode('utf-8'))
file_read.close()
elif oper_code == '2': # 上传操作
file_write = open(str(random.randint(1000, 9999)) + '.txt', 'w', encoding='utf-8')
file_write.write(recv_data)
file_write.close()
print('服务器接收完成!')
elif oper_code == '0': # 已退出
print(recv_data)
from socket import *
while True:
client_socket = socket(AF_INET, SOCK_STREAM)
# 指定要连接的IP
host_port = ('127.0.0.1', 6666)
# 开始连接服务器
client_socket.connect(host_port)
choice = eval(input('请选择操作: 0.退出 1.下载 2.上传 \n'))
if choice == 1:
file_name = input('请输入要下载的文件名:')
# 告诉服务器要下载的文件名
join_data = (str(choice) + ':' + file_name).encode('utf-8')
# 发送数据
client_socket.send(join_data)
# 接收服务器返回的数据
recv_data = client_socket.recv(1024).decode('utf-8')
# 写入本地磁盘
download = open(file_name, 'w', encoding='utf-8')
download.write(recv_data)
download.close()
print('下载完成')
elif choice == 2: # 上传
path_name = input('请输入要上传的文件名:')
# 本地读取
upload = open(path_name, 'r', encoding='utf-8')
upload_data = upload.read()
# 拼接数据结构
data = (str(choice) + ':' + upload_data).encode('utf-8')
# 向服务器发送数据
client_socket.send(data)
upload.close()
print('数据上传成功!')
elif choice == 0:
# 告诉服务器已退出
client_socket.send((str(choice) + ':' + '客户端已退出').encode('utf-8'))
break
print('客户端关闭')
TCP连接时三次握手:
第一次:客户端向服务器端发送连接报文(SYN=1),同时选择一个初始序列号 seq=x,一起发送
第二次:服务器收到报文后向客户端发报文,确认报文为:(ACK=1,SYN=1),确认号为ack=x+1,同时服务器初始化一个序列号:seq=y,一起发送
第三次:客户端向服务器端发送报文(ACK=1),同时发送:ack=x+1,seq=y+1进行确认
TCP断开时四次挥手:开时四次挥手:
第一次:客户端发送释放报文并停止发送数据(FIN=1),带上序列号 seq=u,客户端进入终止等待状态(FIN-WAIT-1)
第二次:服务器收到报文后释放报文,发出确认报文(ACK=1,ack=u+1),并且带上序列号 seq=v,服务器进入关闭等待状态(CLOSE-WAIT)
第三次:服务器在数据传输完毕后发送连接释放报文(FIN=1,ack=u+1),同时发送序列号:seq=w,服务器进入最后确认状态(LAST-ACK)
第四次:客户端收到释放报文后,向服务器发送报文(ACK=1,ack=w+1),发送序列号 seq=u+1,客户端进入时间等待状态(TIME-WAIT)。服务器接收到报文后直接关闭,客户端需要等2**MSL(最长报文段寿命)后结束