入门创建Socket对象
python
"""
案例: 演示socket对象的创建.
网络编程介绍:
概述:
网络编程也叫网络通信, Socket通信, 即: 通信双方都独有自己的Socket对象,
数据在Socket之间通过 数据报包(UDP协议) 或者 字节流(TCP协议) 的形式进行传输.
"""
# 导包
import socket
# 创建Socket对象
# 参1: Address Family, 地址族, 即: Ipv4 还是 IpV6, 默认值: AF_INET(ipv4) AF_INET6(ipv6)
# 参2: Socket Type, Socket类型, 即: TCP 还是 UDP, 默认值: SOCK_STREAM(TCP) SOCK_DGRAM(UDP)
socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(socket_obj)
网编案例
-
服务器端代码
python""" 案例: 网编入门案例, 服务器端给客户端发送消息, 客户端给出回执信息. 服务器端开发流程: 1. 创建服务器端Socket对象. 2. 绑定IP地址和端口号. 3. 设置最大监听数. 4. 等待客户端申请建立连接. 5. 给客户端发送消息. 6. 接收客户端的信息并打印. 7. 释放资源. 细节: 客户端和服务器端是通过 字节流(bytes) 的形式实现的. """ # 导包 import socket # 1. 创建服务器端Socket对象. ipv4, 字节流(TCP) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 绑定IP地址和端口号. server_socket.bind(('192.168.22.51', 10086)) # 3. 设置最大监听数. server_socket.listen(5) # 4. 等待客户端申请建立连接. accept_socket, client_info = server_socket.accept() # 5. 给客户端发送消息. accept_socket.send(b'Welcome To Socket!') # 6. 接收客户端的信息并打印. data = accept_socket.recv(1024).decode('utf-8') print(f'服务器端收到 来自{client_info} 的信息: {data}') # 7. 释放资源. accept_socket.close() # server_socket.close() # 服务器端一般不关闭. -
客户端代码
python""" 案例: 网编入门案例, 服务器端给客户端发送消息, 客户端给出回执信息. 客户端开发流程: 1. 创建客户端Socket对象. 2. 连接服务器端, 指定: 服务器端IP, 端口号. 3. 接收服务器端的信息并打印. 4. 给服务器端发送消息. 5. 释放资源. 细节: 客户端和服务器端是通过 字节流(bytes) 的形式实现的. """ # 导包 import socket # 1. 创建客户端Socket对象. ipv4, TCP协议 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 连接服务器端, 指定: 服务器端IP, 端口号. client_socket.connect(('192.168.22.51', 10086)) # 3. 接收服务器端的信息并打印. data = client_socket.recv(1024).decode('utf-8') print(f'客户端收到: {data}') # 4. 给服务器端发送消息. client_socket.send('Socket很好玩儿, 很有趣, 我很喜欢!'.encode('utf-8')) # 5. 释放资源. client_socket.close()扩展: 设置端口号重用, 目的是: 快速重启服务器(服务器关闭后, 立即释放端口).
参1: 当前的套接字对象, 参2: 选项名, 参3: 该选项的值
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
网编案例_模拟多任务服务器端
python
"""
案例: 网编入门案例, 服务器端给客户端发送消息, 客户端给出回执信息.
服务器端开发流程:
1. 创建服务器端Socket对象.
2. 绑定IP地址和端口号.
3. 设置最大监听数.
4. 等待客户端申请建立连接.
5. 给客户端发送消息.
6. 接收客户端的信息并打印.
7. 释放资源.
细节:
客户端和服务器端是通过 字节流(bytes) 的形式实现的.
"""
# 导包
import socket
# 1. 创建服务器端Socket对象. ipv4, 字节流(TCP)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定IP地址和端口号.
server_socket.bind(('192.168.22.51', 10086))
# 3. 设置最大监听数.
server_socket.listen(5)
while True:
try:
# 4. 等待客户端申请建立连接.
accept_socket, client_info = server_socket.accept()
# 5. 给客户端发送消息.
accept_socket.send(b'Welcome To Socket!')
# 6. 接收客户端的信息并打印.
data = accept_socket.recv(1024).decode('utf-8')
print(f'服务器端收到 来自{client_info} 的信息: {data}')
# print(f'服务器端收到: {data}')
# 7. 释放资源.
accept_socket.close()
# server_socket.close() # 服务器端一般不关闭.
except:
pass
扩展_编解码
python
"""
案例: 演示编解码.
细节:
1. 编码 = 把我们看懂的 转成 我们看不懂的.
'字符串'.encode(码表)
2. 解码 = 把我们看不懂的 转成 我们看懂的.
二进制.decode(码表)
3. 只要乱码了, 原因只有1个, 编解码不同.
4. 英文字母, 数字, 特殊符号无论什么码表都只占1个字节, 中文在gbk占2个字节, utf-8中占3个字节.
5. 二进制数据特殊写法, 即: b'字母 数字 特俗符号', 该方式针对于中文无效.
"""
# 需求1: 编码.
# s1 = '黑马'
s1 = '黑马123abCD!@#'
print(s1.encode()) # b'\xe9\xbb\x91\xe9\xa9\xac123abCD!@#'
print(s1.encode('utf-8')) # b'\xe9\xbb\x91\xe9\xa9\xac123abCD!@#'
print(s1.encode('gbk')) # b'\xba\xda\xc2\xed123abCD!@#'
print('-' * 23)
# 需求2: 解码
bys = b'\xe9\xbb\x91\xe9\xa9\xac123abCD!@#'
print(type(bys)) # <class 'bytes'>
s2 = bys.decode()
s3 = bys.decode('utf-8')
print(s2)
print(s3)
print('-' * 23)
s4 = bys.decode('gbk')
print(s4) # 榛戦┈123abCD!@#
网编案例_文件上传
-
服务器端代码
python""" 案例: 文件上传案例, 服务器端代码. 回顾: 网编服务器端实现流程. 1. 创建服务器端Socket对象. 2. 绑定ip 和 端口号. 3. 设置最大监听数. 4. 等待客户端申请建立连接 5. 读取客户端上传的(文件)数据, 写到目的地文件 6. 释放资源. """ # 导包 import socket # 1. 创建服务器端Socket对象. server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 绑定ip 和 端口号. server_socket.bind(("192.168.22.51", 6666)) # 3. 设置最大监听数. server_socket.listen(5) # 4. 等待客户端申请建立连接 accept_socket, client_info = server_socket.accept() # 5. 读取客户端上传的(文件)数据 # 5.1 关联目的地文件. with open('./data/my.txt', 'wb') as dest_f: # 5.2 循环读取数据 while True: # 5.3 接收客户端上传的文件数据. bys = accept_socket.recv(8192) # 8192字节 = 8kb # 5.4 判断是否读取到数据, 无数据(说明客户端断开连接)结束即可 if len(bys) == 0: break # 5.5 把读取到的数据写入到目的地文件中. dest_f.write(bys) # 7. 释放资源. accept_socket.close() -
客户端
python""" 案例: 网编入门案例, 服务器端给客户端发送消息, 客户端给出回执信息. 客户端开发流程: 1. 创建客户端Socket对象. 2. 连接服务器端, 指定: 服务器端IP, 端口号. 3. 接收服务器端的信息并打印. 4. 给服务器端发送消息. 5. 释放资源. 细节: 客户端和服务器端是通过 字节流(bytes) 的形式实现的. """ # 导包 import socket # 1. 创建客户端Socket对象. ipv4, TCP协议 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 连接服务器端, 指定: 服务器端IP, 端口号. client_socket.connect(('192.168.22.51', 10086)) # 3. 接收服务器端的信息并打印. data = client_socket.recv(1024).decode('utf-8') print(f'客户端收到: {data}') # 4. 给服务器端发送消息. client_socket.send('Socket很好玩儿, 很有趣, 我很喜欢!'.encode('utf-8')) # 5. 释放资源. client_socket.close()
扩展_模拟多任务版文件上传服务器端
多任务简介
-
概述
让多个任务"同时"执行, 目的是: 充分利用CPU资源, 提高程序的执行效率.
-
方式
- 并发: 针对于单核CPU来讲, 多个任务同时请求执行时, CPU做高效切换即可.
- 并行:针对于多核CPU来讲, 多个任务同时执行.
单任务代码演示
python
"""
案例: 演示单任务, 前边不执行完毕, 后边绝对无法执行.
"""
# 1.定义函数A, 输出10次 hello world
def func_a():
for i in range(1000000):
print("hello world")
# 2. 定义函数B, 输出10次 hello python
def func_b():
for i in range(2):
print("hello python")
func_a()
print('-' * 23)
func_b()
多进程入门案例
-
入门案例
python""" 案例: 演示多进程入门案例.一边写代码,一边听音乐 多进程目的: 它属于多任务的一种实现方式, 目的是充分利用CPU资源, 提高程序执行效率. 实现方式: 1. 导包. 2. 创建进程对象, 关联目标函数. 3. 启动进程. """ # 导包 import multiprocessing import time # 1. 定义函数 表示 编写代码. def coding(): for i in range(1, 11): time.sleep(0.1) # 可以模拟耗时操作, 更好的查看多任务的执行效果. print(f'正在敲第 {i} 遍代码!') # 2. 定义函数 表示 听音乐。 def music(): for i in range(1, 11): time.sleep(0.1) print(f'正在听第 {i} 遍音乐......') # 3. 创建两个进程对象, 分别关联上述的两个 目标函数. # 细节: 通过main进程(主进程)来创建子进程. if __name__ == '__main__': # 单任务 # coding() # music() # 进程p1关联 coding函数, p1进程抢到(CPU资源了), 就会执行这个函数. p1 = multiprocessing.Process(target=coding) p2 = multiprocessing.Process(target=music) # 4. 启动进程. 大白话: 表示进程启动了, 就可以开始抢CPU资源了. p1.start() p2.start() -
带参数的多进程代码
python""" 案例: 演示带参数的多进程. 进程传参有两种方式: 方式1: args方式, 接受所有的 位置参数. 方式2: kwargs方式, 接受所有的 关键字参数. """ # 导包 import multiprocessing, time # 需求: 小明一边敲代码, 一边听音乐. # 1. 定义函数, 表示敲代码. def coding(name, num): for i in range(1, num + 1): time.sleep(0.1) print(f'{name} 正在敲第 {i} 行代码...') # 2. 定义函数, 表示听音乐. def music(name, count): for i in range(1, count + 1): time.sleep(0.1) print(f'{name} 正在听第 {i} 首歌...........') # 3.创建主进程(主线程) if __name__ == '__main__': # 4. 创建两个子进程, 分别关联上述的目标函数. p1 = multiprocessing.Process(target=coding, args=('虚竹', 10)) p2 = multiprocessing.Process(target=music, kwargs={'count': 20, 'name': '刘备'}) # 5. 开启子进程. p1.start() p2.start()
获取进程编号
python
"""
案例: 演示获取进程的编号.
进程的编号解释:
概述:
在设备中, 每个程序(进程)都有自己的唯一进程id, 当程序释放的时候, 该进程id也会释放. 即: 进程id是可以重复使用的.
目的:
1. 查看子进程和父进程的关系, 方便 管理.
2. 例如: 杀死指定进程, 创建子进程...
格式:
查看当前进程的pid:
os模块(operating, 系统模块) 的 getpid() get Process id
multiprocessing#current_process()的pid属性
查看当前进程的ppid: parent process id(父进程id)
os#getppid()
细节:
main中创建的进程, 如果没有特殊指定, 它的父进程都是main进程,
而main进程的父进程是 PyCharm程序的pid
"""
# 导包
import multiprocessing, time
import os
# 需求: 小明一边敲代码, 一边听音乐.
# 1. 定义函数, 表示敲代码.
def coding(name, num):
for i in range(1, num + 1):
time.sleep(0.1)
print(f'{name} 正在敲第 {i} 行代码...')
print(f'p1进程的pid: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程id(ppid为) : {os.getppid()}')
# 2. 定义函数, 表示听音乐.
def music(name, count):
for i in range(1, count + 1):
time.sleep(0.1)
print(f'{name} 正在听第 {i} 首歌...........')
print(f'p2进程的pid: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程id(ppid为) : {os.getppid()}')
# 3.创建主进程(主线程)
if __name__ == '__main__':
# 4. 创建两个子进程, 分别关联上述的目标函数.
p1 = multiprocessing.Process(target=coding, args=('虚竹', 10))
p2 = multiprocessing.Process(target=music, kwargs={'count': 20, 'name': '刘备'})
# 5. 开启子进程.
p1.start()
p2.start()
# 6. 查看主进程的信息.
print(f'main进程的pid: {os.getpid()}, {multiprocessing.current_process().pid}, 父进程id(ppid为) : {os.getppid()}')
进程特点_数据隔离
-
代码
python""" 案例: 演示进程的特点. 进程的特点: 1. 进程之间数据是相互隔离的. 因为子进程相当于是父进程的"副本", 会将父进程的"main外资源"拷贝一份, 即: 各是各的. 2. 默认情况下, 主进程会等待子进程执行结束再结束. """ import multiprocessing import time # 需求: 定义1个公共的容器 my_list = [], 一个进程往里边写数据, 另一个进程从里边读数据, 看是否能读取到. # 1. 定义1个公共的容器 my_list = [] my_list = [] # 2. 定义函数, 往容器中添加数据. def write_data(): for i in range(1, 6): my_list.append(i) print(f'添加数据: {i}') # 走到这里, 说明添加完毕, 打印即可. print(f'write_data函数: {my_list}') # [1, 2, 3, 4, 5] # 3. 定义函数, 从容器中读取数据. def read_data(): time.sleep(3) print(f'read_data函数: {my_list}') # [] print('我是main外资源, 看我执行了几次') # my_list[]也是main外资源,相当于给子进程一人一份 # 4. 测试 if __name__ == '__main__': # 5. 创建两个子进程, 分别关联上述的两个函数. p1 = multiprocessing.Process(target=write_data) p2 = multiprocessing.Process(target=read_data) # 6. 启动进程. p1.start() p2.start() # print('我是main内资源, 看我执行了几次')
进程特点_守护进程
python
"""
案例: 演示进程特点之 默认情况下, 主进程会等待子进程执行结束再结束.
进程的特点:
1. 进程之间数据是相互隔离的.
因为子进程相当于是父进程的"副本", 会将父进程的"main外资源"拷贝一份, 即: 各是各的.
2. 默认情况下, 主进程会等待子进程执行结束再结束.
如果要设置主进程结束, 子进程同步结束, 方式如下:
思路1: 设置子进程为 守护进程.
思路2: 强制关闭子进程. 可能会导致子进程变成僵尸进程, 交由Python 解释器自动回收(底层有 init初始化进程来管理维护).
"""
import multiprocessing
import time
# 导包
# 1.定义函数, 表示: 子进程的目标函数.
def work():
for i in range(10):
print('正在努力工作中...')
time.sleep(0.2)
# 2.测试
if __name__ == '__main__':
# 3. 创建子进程, 关联目标函数.
# 细节: 进程的默认命名规则是: Process-编号, 编号是从1开始的.
# p1 = multiprocessing.Process(target=work, name='刘亦菲')
# print(f'p1进程的名字: {p1.name}')
p1 = multiprocessing.Process(target=work)
# 思路1: 设置p1为: 守护进程.
p1.daemon = True # 设置p1为: 守护进程.
# 4.启动进程.
p1.start()
# 5.主进程(main)休眠1秒后, 结束.
time.sleep(1)
# 思路2: 强制关闭子进程.
# p1.terminate()
print('main进程结束了.')
线程入门
-
无参数
python""" 案例: 线程入门案例, 一边听音乐, 一边写代码. 线程的使用步骤: 1. 导包 2. 创建线程对象. 3. 启动线程. 线程和进程的关系: 1. 进程是CPU分配资源的基本单位, 线程是CPU调度资源的最小单位. 2. 线程是依附于进程的, 每个进程至少有1个线程(主线程栈) 3. 进程间数据相互隔离, (同一个进程的)线程间数据可以共享. """ # 导包 import threading, time # 1. 定义函数, 表示: 敲代码. def coding(): for i in range(1, 11): time.sleep(0.1) print(f'正在敲第 {i} 遍代码...') # 2. 定义函数, 表示: 听音乐. def music(): for i in range(1, 11): time.sleep(0.1) print(f'正在听第 {i} 首音乐...') # 3. 测试 if __name__ == '__main__': # 4. 创建两个线程对象, 分别关联上述的两个目标函数. t1 = threading.Thread(target=coding) t2 = threading.Thread(target=music) # 5. 启动线程. t1.start() t2.start() -
带参数
python""" 案例: 线程入门案例, 一边听音乐, 一边写代码. 线程的使用步骤: 1. 导包 2. 创建线程对象. 3. 启动线程. 线程和进程的关系: 1. 进程是CPU分配资源的基本单位, 线程是CPU调度资源的最小单位. 2. 线程是依附于进程的, 每个进程至少有1个线程(主线程栈) 3. 进程间数据相互隔离, (同一个进程的)线程间数据可以共享. """ # 导包 import threading, time # 1. 定义函数, 表示: 敲代码. def coding(name, num): for i in range(1, num + 1): time.sleep(0.1) print(f' {name} 正在敲第 {i} 遍代码...') # 2. 定义函数, 表示: 听音乐. def music(name, count): for i in range(1, count + 1): time.sleep(0.1) print(f' {name} 正在听第 {i} 首音乐*********') # 3. 测试 if __name__ == '__main__': # 4. 创建两个线程对象, 分别关联上述的两个目标函数. t1 = threading.Thread(target=coding, args=('李想', 100)) t2 = threading.Thread(target=music, kwargs={'count':50, 'name':'张三'}) # 5. 启动线程. t1.start() t2.start()
多线程特点_随机性
python
"""
案例: 演示多线程特点.
多线程特点:
1. 线程执行具有随机性, 原因是因为CPU在做着高效的切换.
2. 默认情况下, 主线程会等待子线程结束再结束.
3. (同一个进程的)线程间 数据共享。
4. 多线程操作共享数据, 可能会出现安全问题, 可以用 互斥锁解决。
CPU调度资源的策略:
1.均分时间片
2.抢占式调度
"""
# 需求: 创建多个线程, 多次运行, 观察结果.
# 导包
import threading
import time
# 1.定义多线程的目标函数.
def print_info():
# 1.1 休眠
time.sleep(0.2)
# 1.2 获取当前线程对象.
current_thread = threading.current_thread()
# 1.3 打印当前线程的名字.
print(current_thread.name)
# 2. 测试
if __name__ == '__main__':
# 2.1 创建10个线程, 观察其运行效果.
for i in range(10):
t = threading.Thread(target=print_info)
t.start()
多线程特点_守护线程
python
"""
案例: 演示多线程特点之 守护线程.
多线程特点:
1. 线程执行具有随机性, 原因是因为CPU在做着高效的切换.
2. 默认情况下, 主线程会等待子线程结束再结束.
3. (同一个进程的)线程间 数据共享。
4. 多线程操作共享数据, 可能会出现安全问题, 可以用 互斥锁解决。
"""
# 导包
import threading, time
# 1.定义目标函数.
def work():
for i in range(10):
time.sleep(0.2)
print('工作中...')
# 2. 测试.
if __name__ == '__main__':
# 2.1 创建(子)线程对象.
# (守护线程)写法1: daemon属性
# t = threading.Thread(target=work, daemon=True)
# (守护线程)写法2: setDaemon()函数, 已过时(暂时还支持, 以后的新版本中可能会被移除掉).
# t = threading.Thread(target=work)
# t.setDaemon(True)
# (守护线程)写法3: daemon属性
t = threading.Thread(target=work)
t.daemon = True
# 2.2 启动线程.
t.start()
# 2.3 设置主线程休眠时间1秒
time.sleep(1)
# 2.4 设置主线程的结束标记.
print('主线程结束了!')
多线程特点_数据共享
python
"""
案例: 演示多线程特点之 数据共享.
多线程特点:
1. 线程执行具有随机性, 原因是因为CPU在做着高效的切换.
2. 默认情况下, 主线程会等待子线程结束再结束.
3. (同一个进程的)线程间 数据共享。
4. 多线程操作共享数据, 可能会出现安全问题, 可以用 互斥锁解决。
"""
# 需求: 定义全局变量my_list = [], 定义两个目标函数分别实现添加, 查看数据. 最后创建两个线程, 分别执行对应的任务, 观察结果.
# 导包
import threading, time
# 1.定义全局变量
my_list = []
# 2. 定义目标函数, 添加数据.
def write_data():
for i in range(1, 6):
my_list.append(i)
print("写入数据: ", i)
print(f'write_data函数: {my_list}')
# 3. 定义目标函数, 查看数据.
def read_data():
# 休眠, 即: 等待write_data()执行结束在结束.
time.sleep(2)
print(f'read_data函数: {my_list}')
# 4. 测试
if __name__ == '__main__':
# 4.1 创建线程对象, 并且启动线程.
t1 = threading.Thread(target=write_data)
t2 = threading.Thread(target=read_data)
# 4.2 启动线程.
t1.start()
t2.start()
多线程特点_互斥锁
-
图解

-
代码
python""" 案例: 演示多线程共享全局变量, 可能出现的问题. 多线程共享全局变量, 出现问题的问题: 累加次数不够. 产生原因: 线程1还没有来记得执行完(一个完整的动作)前, 被线程2抢走了资源, 就可能出问题. 解决方案: 加锁思想, 即: 互斥锁. 细节: 使用互斥锁的时候, 要在合适的时机释放所, 否则可能出现 死锁 或者 锁不住的情况. """ # 需求: 定义两个函数, 分别对全局变量累加100W次, 创建两个线程, 关联这两个函数, 执行看效果. # 导包 import threading # 1.定义全局变量. global_num = 0 # 创建线程锁. mutex = threading.Lock() # mutex2 = threading.Lock() # 2.定义目标函数1, 对全局变量累加100W次. def target_fun1(): mutex.acquire() # 加锁 # 2.1 声明为全局变量 global global_num # 2.2 遍历100W次, 对全局变量进行累加. for i in range(1000000): # 2.3 具体的累加动作 global_num += 1 # 2.4 累加完毕后, 打印结果. print(f'target_fun1函数结果: {global_num}') mutex.release() # 释放锁 # 3.定义目标函数2, 对全局变量累加100W次. def target_fun2(): mutex.acquire() # 加锁 global global_num for i in range(1000000): global_num += 1 print(f'target_fun2函数结果: {global_num}') mutex.release() # 释放锁 # 4.测试. if __name__ == '__main__': # 4.1 创建两个线程, 分别关联上述的两个目标函数. t1 = threading.Thread(target=target_fun1) t2 = threading.Thread(target=target_fun2) # 4.2 开启线程. t1.start() t2.start()
进程和线程对比
python进程和线程的区别: 1. 线程依赖进程, 进程是CPU分配资源的基本单位, 线程是CPU调度资源的基本单位. 2. 进程更消耗资源, 不能共享全局变量, 相对更稳定. 3. 线程更轻量级, 可以共享全局变量, 相对更灵活.