Socket(套接字)
介绍 UDP 和 TCP 协议之前,先熟悉下 socket 的基本概念。
- 基本概念
Socket 是通信的基石,是支持 TCP/IP 协议的网络通信的基本操作单元,在网络通信过程中端点的一种抽象表示。网络中使用 Socket 传输数据是一种特殊的网络 I/O。
- 工作模式
打开open -> 读写write/read -> 关闭close
- 五种信息
socket 包括了数据传输必须的五元组,分别为源IP、源端口、目的IP、目的端口和协议号
- 通信机制
基于流(stream)或者基于数据报(datagram)
- python 中使用
import socket
socket.socket(...)
UDP
UDP(User Datagram Protocol) 用户数据报协议,是一种面向无连接的协议,提供简单不可靠的信息传输服务,发送后不会确认信息是否到达。
UDP 通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中"写信"。
- 使用 socket 使用 UDP 的收发数据,先看原理图
- 客户端代码实现
# 客户端 发送数据
# 1.创建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
send_data = input("请输入要发送的数据:")
if send_data == 'exit':
break
# 2.发送数据
udp_socket.sendto(send_data.encode(), ("192.168.1.110", 8080)) # 对方的ip和port
# 3.关闭套接字
udp_socket.close()
- 服务端代码实现
# 服务端 接收数据
HOST = '' # 空为localhost
PORT = 7788
ADDR = (HOST, PORT)
BUFSIZE = 1024 # 一次接收最大的尺寸
# 1.创建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.绑定本地套接字信息
udp_socket.bind(ADDR) # 绑定必须是本机的ip和port
while True:
# 3.接收数据
data, addr = udp_socket.recvfrom(BUFSIZE) # 返回接收数据和发送方的地址信息
if data.decode() == 'exit':
print("退出")
break
# 打印接收的数据
print(f"{addr[0]}:{addr[1]}", data.decode())
# 4.关闭套接字
udp_socket.close()
套接字是可以同时收发数据,是全双工状态,在调用
recvform
之前收到数据,操作系统会将数据暂时储存起来,等到程序调用recvform
取出数据,在调用recvform
之后未接收到数据,程序会进入阻塞状态,等待数据的到来。
TCP
TCP(Transmission Control Protocol)传输控制协议,是一种面向连接的,可靠的、基于字节流的传输层通信协议。
TCP 通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP 通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中"打电话"。 这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用 UDP 协议。
特点:
-
面向连接(使用三次握手建立连接,连接已创建才作传输,采用四次挥手断开连接。)
-
有序数据传输
-
重发丢失的数据包
-
舍弃重复的数据包
-
无差错的数据传输
-
阻塞/流量控制
使用 socket 实现 TCP 的收发数据,先看原理图
- 客户端代码实现
# 客户端
IPADDR = "192.168.1.110"
PORT = 7788
# 创建套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器,创建连接
tcp_socket.connect((IPADDR, PORT))
# 收发数据
send_data = input("输入发送的数据:")
tcp_socket.send(send_data.encode())
# 关闭套接字,关闭连接
tcp_socket.close()
- 服务端代码实现
# 服务端
IPADDR = “” # 默认localhost
PORT = 7788
BUFSIZE = 1024 # 一次接收最大的尺寸
print('server start...')
# 创建套接字(监听套接字) =》买个手机
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定本地信息 =》 插入手机卡
tcp_socket.bind((IPADDR, PORT))
# 将手机设为正常,能够响铃 =》 让默认的套接字由主动变为监听
tcp_socket.listen(128) # 同一时间,最大允许连接128个客户端
while True: # 循环为多个客户端服务
print("waitting connected...")
# 等待电话到来 =》 等待别人电话的到来,此时程序会被阻塞,等待客户端连接
new_client_socket, client_addr = tcp_socket.accept() # 新的专门为客户端服务的socket,客户端的addr
# 接收客户端发送过来的请求,此时程序会阻塞等待客户端发送数据
print("waitting recive data...")
while True: # 循环为同一个客户端服务多次
recv_data = new_client_socket.recv(BUFSIZE)
# 如果recv 解堵塞,有两种方式
# 1.客户端发送过来数据
# 2.客户端调用close断开连接
if recv_data:
print(f"【{client_addr[0]}:{client_addr[1]}】", recv_data.decode())
# 回送一部分数据给客户端
new_client_socket.send('收到'.encode())
else:
break
# 关闭套接字
new_client_socket.close()
print("connect closed")
tcp_socket.close()
print('server end...')
监听套接字负责等待有新的客户端进行连接
accept
产生的新的套接字用来为客户端服务 如果将监听套接字关闭了,那么会导致不能再次等待新客户端的到来,accept()
会失败 关闭accept
返回的新的套接字,意味着不再为这个客户端服务。