Python-Socket

一、参考

1. python之socket编程

2. socket编程 (byhy.net)

二、相关概念

我们作为程序员,只要知道,我们的程序如何把 所要发送的信息 交给 快递公司取件人 , 如何从 快递公司送件人 手中获取信息。

那么 和我们的 应用程序 直接打交道的 快递公司取件人快递公司送件人 到底是什么? 就是操作系统 提供的 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接收到的必定是发送方发送的完整消息。

相关推荐
.生产的驴4 分钟前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑13 分钟前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
追逐时光者1 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
方圆想当图灵1 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫1 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
嘻嘻嘻嘻嘻嘻ys1 小时前
《Spring Boot 3 + Java 17:响应式云原生架构深度实践与范式革新》
前端·后端
异常君1 小时前
线程池隐患解析:为何阿里巴巴拒绝 Executors
java·后端·代码规范
mazhimazhi1 小时前
GC垃圾收集时,居然还有用户线程在奔跑
后端·面试
Python私教1 小时前
基于 Requests 与 Ollama 的本地大模型交互全栈实践指南
后端
ypf52081 小时前
Tortoise_orm与Aerich 迁移
后端