PY进阶——网编与多线程

网络编程

用来实现不同计算机上运行的程序间进行数据交互
主要应用于文件传输、设备数据传输

IP/端口号/协议

IP确定设备,端口号确定服务,协议保障数据传输的规则

Soket与TCP开发流程

TCP通信:三次握手建立连接,四次挥手断开连接
建立连接后进行数据传输,该协议数据传输可靠
Socket是进程之间通信的一个工具

1
2
3
4
5
6
7
8
9
10
#导入socket模块
import socket
# socket(AddressFamily,Type)
"""
上述创建一个socket对象
参数AddressFamily(地址族)默认选择AF_INET(ipv4),AF_INT6(ipv6)用于进程间通信,
Type表示套接字类型(默认SOCK_STREAM,用于TCP协议;SOCK_DGRAM用于UDP)
"""
#创建Socket对象
socket_obj=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

TCP开发流程

服务端:socket()->bind()->listen()->accept()->等待连接->(send()<=>recv())->close()
bind()指定服务端(ip,端口号),listen()最大监听数(最大数为128),accept()等待监听(会返回一个包含负责和客户端交互的socket与客户端信息的元组,也称为connection对象),即当客户端开始连接时,会分配单独的socket对象与客户端做交互
send(data)发送数据(data为bytes二进制形式),recv(bufsize)接收数据(返回bytes二进制形式,bufsize指定最大的数据量),close()关闭连接,对服务端来说关闭的是那个单独的socket
客户端:socket()->connect()->(recv()<=>send())->close()
connect()向(ip,端口号)发起连接

除上述外还有同时应用于服务端/客户端的函数为**setsokopt(level,optname,value)**用于设置端口号复用,当程序结束后立即释放端口号,level=SOL_SOCKET(当前套接字对象),optname=SO_REUSEADDR(重用端口),value=True

TCP编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket
# 服务端
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定数据
s.bind(('127.0.0.1', 9092))
# 设置监听数目
s.listen(5)
# 监听等待连接
conn, addr = s.accept()
#发送消息
conn.send(b'Hello, world!')#对英文字符转二进制
#接收信息
data=conn.recv(1024).decode('utf8')
print(data)
#关闭连接
conn.close()
1
2
3
4
5
6
7
8
9
10
11
12
import socket
# 客户端
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定数据
s.connect(('127.0.0.1', 9092))
#发送消息
s.send('你好'.encode('utf-8'))
#接收信息
data=s.recv(1024).decode('utf8')
print(data)
#关闭连接
s.close()

上述代码是单线程的
当客户端close后,服务端的recv会解阻塞,返回的数据长度为0,以此判断客户端是否下线

模拟多任务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket
# 服务端
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定数据
s.bind(('127.0.0.1', 9092))
# 设置监听数目
s.listen(5)
# 监听等待连接
while True:
try:#防止存在连接错误
conn, addr = s.accept()
conn.senda(b'Hello, world!')
data=conn.recv(1024).decode('utf8')
print(data)
conn.close()
except:
pass

上述代码是模拟多任务,实际上是分时操作

编解码

英文字母、数字、特殊符号在所有码表中只占一个字节,中文在gbk占两个字节,utf中占3字节
二进制特殊编码:对字母,数字,特殊符号可使用**b’str’**做编码
常规编码

1
2
3
4
5
6
s1='你好22hello!'
# 编码
s1.encode('utf-8')
s2=s1.encode('gbk')
# 解码
s2.decode('gbk')

文件传输例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket
# 客户端
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定数据
s.connect(('127.0.0.1', 9092))
#发送消息
with open('./data/text.txt', 'rb') as f:
while True:
byes = f.read(8192)
if len(byes) ==0:
break
s.send(byes)
# 发送完毕后主动关闭连接
s.shutdown(socket.SHUT_WR)
data=s.recv(1024).decode('utf8')
print(data)
#关闭连接
s.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import socket
# 服务端
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定数据
s.bind(('127.0.0.1', 9092))
# 设置监听数目
s.listen(5)
count=0
while True:
count+=1
# 监听等待连接
conn, addr = s.accept()
try:
# 关联目的地文件
with open('./data/'+str(count)+'.txt', 'wb') as dest_f:
#接收信息
while True:
byes=conn.recv(8192)
# 无数据时说明客户端已断开连接
if len(byes)==0:
break
dest_f.write(byes)
print('文件接收成功')
conn.send('文件上传成功'.encode('utf-8'))
conn.close()
except:
conn.send('文件上传失败'.encode('utf-8'))
break

多任务

多任务同时执行能够充分利用CPU效率,提高执行效率
多任务是在同一时间内执行多个任务
并发:在一段时间内交替执行任务(单核CPU实现多任务效果,CPU做高效切换)
并行:在一段时间内同时执行多个任务(多核cpu)

进程

进程是cpu分配资源的最小单位
比如一个正在运行的程序至少有一个进程
Process(target=None,name=None,args=(),kwargs={})
target调用对象,子进程要执行的任务(回调函数入口地址)
name子进程名称
args以元组形式向子任务函数传参,顺序一致
kwargs以字典形式向子任务传参

1
2
3
4
5
6
7
# 创建多进程
# 导入进程工具包
import multiprocessing as mp
# 创建实例化对象
m=mp.Process()
# 启动进程
m.start()

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 创建多进程
# 导入进程工具包
import multiprocessing as mp
import time
def reading(name):
for i in range(1,11):
time.sleep(0.1)
print(f'reading {name} in {i}')
def writing(name):
for i in range(1,11):
time.sleep(0.1)
print(f'writing {name} in {i}')
if __name__ == '__main__':
r=mp.Process(target=reading,args=('book',))
w=mp.Process(target=writing,args=('title',))
r.start()
w.start()
"""
writing title in 1
reading book in 2
writing title in 2
reading book in 3
writing title in 3
reading book in 4
writing title in 4
reading book in 5
writing title in 5
reading book in 6
writing title in 6
reading book in 7
writing title in 7
reading book in 8
writing title in 8
reading book in 9
writing title in 9
reading book in 10
writing title in 10
"""

获取进程编号

一个进程有唯一的标识-进程编号
获取进程编号是验证主进程与子进程的关系,得知子进程是由哪个主进程创建的
也可以杀死指定进程,创建子进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 获取当前进程编号 os.getpid()或者mp.current_process().pid
# 获取当前父进程编号os.getppid()
import multiprocessing as mp
import os
import time
def reading(name):
for i in range(1,11):
time.sleep(0.1)
print(f'reading {name} in {i}')
print(os.getpid(),mp.current_process().pid,os.getppid())
def writing(name):
for i in range(1,11):
time.sleep(0.1)
print(f'writing {name} in {i}')
print(os.getpid(), mp.current_process().pid, os.getppid())
if __name__ == '__main__':
r=mp.Process(target=reading,args=('book',))
w=mp.Process(target=writing,args=('title',))
r.start()
w.start()
r.join()
w.join()
print(os.getpid(), mp.current_process().pid, os.getppid())
# 90776 90776 97956
# 92172 92172 97956
# 97956 97956 88604

main中创建的进程没有特殊指定,它的父进程都是main进程,main进程的父进程是IDE

进程注意点

进程之间不共享全局变量:即进程对全局变量的操作不会污染变量,进程会将main的外资源拷贝一份一起执行
主进程会等待所有子进程执行结束后再结束
为了不让主进程等待子进程,可以为子进程设置守候进程或者主动终止子进程(不推荐,会成为僵尸进程,不会立即释放资源)

1
2
3
4
# 守护进程
进程名.daemon=True
# 主动关闭
进程名.terminate()

线程

线程是cpu调度资源(调度已分配资源)上的基本单位,即分配进程内的资源
每个进程至少都有一个线程(主线程),在这之外我们还能在进程内创建自定义线程(启动后才能抢资源)

1
2
3
4
5
import threading as tp
# 创建实例化对象
t=tp.Thread()
# 启动进程
t.start()

线程注意点

线程之间的执行是无序的
默认主线程会等待所有子线程执行结束后结束
要设置守护线程可以,会在主线程结束后直接结束子线程

1
t=tp.Thread(daemon=True)

(同一进程)线程之间共享全局变量
线程之间共享全局数据出现错误(使用互斥解决)

互斥锁

1
2
3
4
5
6
# 创建
mutex=threading.Lock()
# 上锁
mutex.acquire()
# 释放
mutex.release()

死锁:未在合适位置释放锁,导致程序无法向下运行

进程与线程

关系
线程依附于进程
一个进程默认一条线程,进程内可以创建多线程
区别
进程之间不共享全局变量
线程之间共享全局变量(存在互斥,使用互斥锁)
创建进程的资源开销比创建线程大
进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位
线程不呢独立执行,存在于进程
py中多进程开发比单进程多线程开发稳定性要强

进程与线程都是程序(CPU)实现多任务的手段


PY进阶——网编与多线程
http://example.com/2026/03/27/PY进阶——网编与多线程/
作者
印星
发布于
2026年3月27日
许可协议