一、参考
二、相关概念
我们作为程序员,只要知道,我们的程序如何把 所要发送的信息 交给 快递公司取件人
, 如何从 快递公司送件人
手中获取信息。
那么 和我们的 应用程序 直接打交道的 快递公司取件人
和 快递公司送件人
到底是什么? 就是操作系统 提供的 socket 编程接口
发送信息的应用程序,通过 socket 编程接口
把信息给操作系统的TCP/IP协议栈通讯模块;
通讯模块一层层传递给 其他通讯模块(网卡驱动等),最后再通过网卡等硬件设备发送到网络上去;
经过 网络上路由器的一次次转发,最终到了 目的程序 所在的 计算机(或者手机等设备) , 再通过 其 操作系统的 TCP/IP协议栈通讯模块 一层层上传。
最后接收信息的程序,通过 socket 编程接口
接收到了 传输的信息。
这个过程可以用下图来表示
我们前面使用过 requests库 发送 HTTP请求消息,其实 requests库底层也是使用的socket编程接口发送HTTP请求消息。
HTTP 传输的消息 底层也是通过 TCP/IP 协议 传输的, HTTP 加上了一些额外的规定, 比如传输消息的格式。
就像我们发快递的时候做了些额外的处理。比如 把物品 放到一个盒子里。
三、快速入门
3.1 客户端
ini
# === TCP 客户端程序 client.py ===
from socket import *
IP = '127.0.0.1'
SERVER_PORT = 50000
BUFLEN = 1024
# 实例化一个socket对象,指明协议
dataSocket = socket(AF_INET, SOCK_STREAM)
# 连接服务端socket
dataSocket.connect((IP, SERVER_PORT))
while True:
# 从终端读入用户输入的字符串
toSend = input('>>> ')
if toSend =='exit':
break
# 发送消息,也要编码为 bytes
dataSocket.send(toSend.encode())
# 等待接收服务端的消息
recved = dataSocket.recv(BUFLEN)
# 如果返回空bytes,表示对方关闭了连接
if not recved:
break
# 打印读取的信息
print(recved.decode())
dataSocket.close()
3.2 服务端
python
# === TCP 服务端程序 server.py ===
# 导入socket 库
from socket import *
# 主机地址为空字符串,表示绑定本机所有网络接口ip地址
# 等待客户端来连接
IP = '127.0.0.1'
# 端口号
PORT = 50000
# 定义一次从socket缓冲区最多读入512个字节数据
BUFLEN = 512
# 实例化一个socket对象
# 参数 AF_INET 表示该socket网络层使用IP协议
# 参数 SOCK_STREAM 表示该socket传输层使用TCP协议
listenSocket = socket(AF_INET, SOCK_STREAM)
# socket绑定地址和端口
listenSocket.bind((IP, PORT))
# 使socket处于监听状态,等待客户端的连接请求
# 参数 8 表示 最多接受多少个等待连接的客户端
listenSocket.listen(8)
print(f'服务端启动成功,在{PORT}端口等待客户端连接...')
dataSocket, addr = listenSocket.accept()
print('接受一个客户端连接:', addr)
while True:
# 尝试读取对方发送的消息
# BUFLEN 指定从接收缓冲里最多读取多少字节
recved = dataSocket.recv(BUFLEN)
# 如果返回空bytes,表示对方关闭了连接
# 退出循环,结束消息收发
if not recved:
break
# 读取的字节数据是bytes类型,需要解码为字符串
info = recved.decode()
print(f'收到对方信息: {info}')
# 发送的数据类型必须是bytes,所以要编码
dataSocket.send(f'服务端接收到了信息 {info}'.encode())
# 服务端也调用close()关闭socket
dataSocket.close()
listenSocket.close()
3.3 效果演示
当客户端发送数据给服务端后,服务端会反馈给客户端接收到的消息
注意:
开启socket服务后,会开启三个socket服务
第一行socket服务表示服务端等待客户端的连接,状态为LISTENING
服务端一旦连接成功后,会建立两个socket(对应第二行和第三行),状态均为ESTABLISHED
他俩自己的端口号和发送给其他的端号互为相反
PS Z:\python> netstat -ano|findstr 60000
TCP 127.0.0.1:60000 0.0.0.0:0 LISTENING 18064
TCP 127.0.0.1:50764 127.0.0.1:60000 ESTABLISHED 23812
TCP 127.0.0.1:60000 127.0.0.1:50764 ESTABLISHED 18064
四、应用消息格式
对于使用TCP协议传输信息的程序来说,格式定义一定要明确规定 消息的边界
。
因为 TCP协议传输的是 字节流(bytes stream)
, 如果消息中没有指定 边界 或者 长度,接收方就不知道一个完整的消息从字节流的 哪里开始,到 哪里结束。
指定消息的边界有两种方式:
-
用特殊字节作为消息的结尾符号
可以用消息内容中不可能出现的字节串 (比如
FFFFFF
) 作为消息的结尾字符。 -
在消息开头某个位置,直接指定消息的长度
比如在一个消息的最前面用2个字节表示本消息的长度。
UDP协议通常不需要指定消息边界,因为UDP是数据报协议,应用程序从socket接收到的必定是发送方发送的完整消息。