通信
IP地址与端口号
ip地址是网络中电脑的唯一标识
- 通过 IP地址 可以找到 对方的电脑
- 通过 端口号 找到电脑上的 应用软件
- 端口号范围:
1-65535
- 知名端口号:
1024
之前的端口号, 被众所周知的端口号 - 动态端口号:
1024
之后的端口号, 被系统动态分配的端口号
查看端口号命令
netstat -ano # widows
netstat -an # Linux/mac
套接字
真正的应用程序中, 通过 套接字来收发数据
- UDP 套接字
- TCP 套接字
发送数据,需要编码(
encode
),str
字符串=====>
二进制字节流byte
接收数据,需要解码(
decode
), 二进制字节流byte
=====>
str
字符串
UDP通信
# 构建UDP客户端
import socket
# 1. 构建udp套接字
# AF_INET: 网络 进程间通信
# SOCK_DGRAM: 声明套接字类型 为 UDP套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 发送数据
# 要发送的数据, 需要编码
data = 'hello'.encode('utf8')
# 对方的ip和端口号,格式必须是元组
address = ('127.0.0.1', 8099)
udp_socket.sendto(data, address)
# 3. 关闭套接字
udp_socket.close()
import socket
# 1. 构建udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 本程序自己 绑定 ip和端口号
# 不绑定之前, 端口号每次都会发生改变, 绑定之后,端口号固定为8000
udp_socket.bind(("", 8000))
# 3. 接收数据, 接收的数据需要解码
data, address = udp_socket.recvfrom(1024) # 1024,指字节大小, 每次最多接受1024字节的数据
print(address) # 对方的ip和端口号
print(data.decode('utf8')) # 对方发送的数据, 需要解码
# 4. 关闭套接字
udp_socket.close()
循环收发数据
import socket
# 1. 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定ip和端口号, 所有的地址信息,都是元组格式
udp_socket.bind(('', 8001)) # 上次运行代码没有正常关闭套接字,下次出错,修改端口号即可
while True:
# 3. 发送数据
send_data = input("输入要发送的数据")
if send_data == '拜拜':
break
udp_socket.sendto(send_data.encode('utf8'), ('127.0.0.1', 8080))
# 4. 接收数据
recv_data, address = udp_socket.recvfrom(1024)
recv_data = recv_data.decode('utf8')
if recv_data == '拜拜':
break
print("收到来自{}的数据:{}".format(address, recv_data))
# format 格式化输出, 不用考虑数据类型, 直接占位使用即可
# 5. 关闭套接字
udp_socket.close()
TCP通信
TCP
VS UDP
- TCP 面向链接, UDP 无链接
- TCP安全可靠, UDP不安全可靠
- TCP速度较慢,UDP速度较快
- TCP一对一通信, UDP支持一对一、一对多、多对多
- TCP耗费较多系统资源,UDP耗费系统资源较少
客户端
# clinet: 客户端
import socket
# 1. 创建TCP套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 客户端主动向服务器 发起链接请求, 服务器的地址信息同样是元组格式
tcp_socket.connect(('127.0.0.1', 8000))
# 3. 发数据
tcp_socket.send('hello'.encode('utf8'))
# 收数据
data = tcp_socket.recv(1024).decode('utf8')
print(data)
# 4. 关闭套接字
tcp_socket.close()
服务器端
#server: 服务器
import socket
# 1. 创建tcp服务器套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 服务器需要绑定端口号,方便客户端链接
server_socket.bind(("", 8000)) # 绑定之后,服务器端口号固定为8000
# 3. 不知道客户端什么时候发起请求, 服务器需要一直监听等待客户端的请求
server_socket.listen(128) # 同一时刻,最多可以处理128个请求
# 4. 接收客户端的请求, 并分发任务,让其他套接字与客户端通信
client_socket, client_address = server_socket.accept()
# server_socket 套接字不负责具体的收发数据,
# 只负责将客户端请求交给其他套接字处理
# 5. 接收数据, 收发任务具体是由 client_socket套接字完成
send_data = input("输入发送的数据:")
client_socket.send(send_data.encode('utf8'))
recv_data = client_socket.recv(1024).decode('utf8')
print("收到来自客户端{}的数据:{}".format(client_address, recv_data))
# 6. 关闭套接字
client_socket.close()
server_socket.close()
错误类型
1.如果代码上次没有正常关闭,下次再绑定同样的端口号会报错 :
[WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
把端口号修改即可,过一段时间, 系统会自动回收上一个端口号
- 编码方式不一样
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0: invalid start byte
,发送时的 编码方式 一定 要和接收数据时的 解码方式 对应起来,都使用
utf8
即可
- 先启动服务器,后启动客户端, 客户端 去 主动链接 服务器