PythonNet_04_bj

本文深入探讨了网络编程的基本概念,包括HTTP服务器的工作原理、阻塞与非阻塞I/O、多路复用技术如select和epoll的使用,以及本地套接字的创建流程。同时,文章详细讲解了多任务编程的意义、实施方案(多进程和多线程)、进程的特征和状态转换,以及进程创建和退出的机制。

前情回顾
1.httpserver (HTTP协议)
*接收浏览器的连接
*将接收内容解析
*组织响应内容
*将响应内容回发给浏览器

2.阻塞IO 默认形态

3.非阻塞IO 和超时检测

4.IO多路复用
目的 : 当程序中有多个IO事件的时候提高IO的执行效率

方法 :同时监控多个IO事件,当有IO 事件可以运行则处理可执行的IO

代码实现:select poll epoll


epoll
使用方法 : 代码基本与poll相同

  • 将生成对象的 poll() 函数 变为 epoll()
  • 将register注册IO事件时 关注的事件类别改为epoll类别

区别:
epoll 效率要高于 poll和select
epoll 的关注触发方式多一些

01_epoll_server.py

from socket import *
from select import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((“127.0.0.1”,8888))
s.listen(5)

#创建epoll对象
p = epoll()

建立通过fileno查找IO对象的地图

fdmap = {s.fileno()?}
#添加关注
p.register(s,EPOLLIN | EPOLLERR)

while True:
#进行监控
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print(“Connect from”,addr)
#注册新的套接字
p.register(c,EPOLLIN)
#维护地图更新
fdmap[c.fileno()] = c
elif event & EPOLLIN:
data = fdmap[fd].recv(1024)
if not data:
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send(‘收到了’.encode())

本地套接字

linux下文件类型:
b(块设备文件) c(字符设备文件) d(目录)
-(普通文件) l(链接文件) s(套接字文件) p(管道文件)

作用 : 用于本地不同的程序间进行通信

本地套接字创建流程:
1.创建套接字对象
sockfd = socket(AF_UNIX,SOCK_STREAM)
2.建立套接字文件
sockfd.bind(path) 绑定一个文件
3.监听
4.接收发送消息

cookie

os.path.exists(path)
功能: 判断一个文件是否存在
参数: 文件位置
返回值: 存在返回True 否则返回False

os.unlink() os.remove()
功能:删除一个文件
参数:要删除的文件

In [2]: os.path.exists("./re")
Out[2]: True

In [3]: os.unlink("./re")

02_unix_recv.py

from socket import *
import os

#确定使用哪个套接字文件
sock_file = ‘./sock_file’

#如果已经存在同名的文件则删除
if os.path.exists(sock_file):
os.unlink(sock_file)

#创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#绑定套接字文件
sockfd.bind(sock_file)
#监听
sockfd.listen(5)

#消息收发
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if data:
print(data.decode())
c.send(b"Receive your message")
else:
break
c.close()
sockfd.close()

02_unix_send.py

from socket import *

#确保和另一端使用相同的套接字文件
sock_file = “./sock_file”

#本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#链接另外一端
sockfd.connect(sock_file)

#发收消息
while True:
msg = input(">>")
if msg:
sockfd.send(msg.encode())
print(sockfd.recv(1024).decode())
else:
break
sockfd.close()

多任务编程

意义 : 充分的利用计算机资源提高程序的运行效率

定义 : 通过应用程序利用计算机的多个核心达到同时执行多个
任务的目的,以此来提升程序的执行效率

实施方案 : 多进程 多线程

并行: 多个计算机核心在同时处理多个任务,这多个任务间是并行关系

并发:同时处理多个任务,内核在任务间不断的切换,达到好像
都在处理运行的效果

进程:程序在计算机中的一次执行过程

程序 : 是一个可执行文件,是静态的,占有磁盘,不占计算机
的运行资源
进程 : 进程是一个动态的过程描述,占有计算机的资源,有一
定的生命周期

  • 同一个程序的不同运行过程是不同的进程。因为分配的计算机资源不同,
    生命周期也不同

进程的创建流程
1.用户空间运行一个程序,发起进程的创建
2.操作系统接受用户申请开启进行创建
3.操作系统分配计算机资源,确定进程状态
4.将新创建的进程交给用户使用

cpu时间片
如果一个进程占有计算机核心,我们称为该进程占有cpu时间片。
多个任务实际会对cpu内核进行争夺,由操作系统分配cpu资源

进程信息
PCB(进程控制块):在*nix操作系统中,进程创建后会自动在内存
中产生一个空间存放进程信息,称为PCB

进程信息:进程的ID 进程占有内存位置 创建时间 创建用户…

查看命令:ps -aux

PID(process ID) : 在操作系统中进程的唯一标志,大于0的整数,
由系统自动分配

进程的特征
* 进程是操作系统分配计算机资源的最小单位
* 每个进程有自己单独的虚拟内存空间
* 进程之间的执行上相互独立,互不影响

进程的状态

三态
* 就绪态:进程具备执行条件,等待系统分配处理器资源
* 运行态:进程占有cpu处于运行的状态
* 等待态:进程暂时不具备运行条件,需要阻塞等待

五态 (在三态基础上增加新建态和终止态)
* 新建态 :创建一个新的进程,获取资源的过程
* 终止态 :进程执行结束,资源释放回收的过程

ps -aux  ----> STAT 表示进程状态
 
    D  等待态  (不可终端等待)
	S  等待态   (可终端等待)
	T  等待态   (暂停状态)
	R  运行态   (包含就绪态)
	Z  僵尸态  

	+  前台进程 在终端运行
	<  高优先级
	N  低优先级
	l  有进程链接
	s  会话组

进程优先级

优先级决定了一个进程的执行权限和占有资源的优先程度

查看优先级 :
top : 动态查看进程优先级 摁 < > 翻页

优先级取值范围 -20 — 19 -20最高

nice : 以指定的优先级运行一个程序
	e.g.  nice  -9   ./while.py
	      nice  --9  ./while.py   -9的优先级运行

#!/usr/bin/python3
from time import sleep,ctime

while True:
sleep(2)
print(ctime())

父子进程

在系统中除了初始化进程每个进程都有一个父进程,可能有0个
或者多个子进程。由此形成进程的父子关系。我们认为每个进程
都是父进程创造的。

查看进程树 : pstree
查看父进程PID : ps -ajx

要求 : 什么是进程,进程和程序的区别
了解进程的特征
清楚进程每种状态,及状态转换

需求 : 编写一个程序能够同时做多件任务

方案 : 写一个程序,根据需要在程序内不可以创建多个进程完成任务

import os

pid = os.fork()
功能 : 创建一个新的进程
参数 : 无
返回值 :失败返回一个负数 -1
成功 : 在原有进程中返回新进程的PID号
在新进程中返回0

  • 子进程会复制父进程全部代码段,包括fork之前产生的内存空间
  • 子进程从fork的下一句开始执行
  • 父子进程通常会根据fork返回值的差异选择执行不同的代码
  • 子进程虽然复制父进程的代码空间,但是有自己的特有属性
    比如 PID号 PCB等
  • 父子进程在执行上互补干扰,执行顺序不确定
  • 父子进程空间独立,在本进程中对空间的操作不会影响到其他进程

import os
from time import sleep

fork之前的代码只有父进程会执行

print("*******************")
#fork之前产生内存空间的存储,子进程也会有
a = 1
#创建新的进程
pid = os.fork()

if pid < 0:
print(“创建进程失败”)

只有子进程执行的部分

elif pid == 0:
sleep(1)
print("a = ",a)
print(“新创建的进程”)

只有父进程会运行的部分

else:
sleep(5)
print(“原来的进程”)

#if结构外的代码父子进程都会执行
print(“程序执行完毕”)

*******************

a = 1

新创建的进程

程序执行完毕

原来的进程

程序执行完毕


import os
from time import sleep

pid = os.fork()

a = 1

if pid < 0:
print(“Create Process failed”)
elif pid == 0:
#子进程中变量修改不会影响父进程
print("a = ",a)
a = 10000
print(“This is Child process”)
else:
sleep(1)
print(“This is parent process”)
print(“parent a =”,a)

a = 1

This is Child process

This is parent process

parent a = 1

进程相关函数使用

获取进程号
os.getpid()
功能 :获取当前进程的进程号
返回值 : 返回进程号

os.getppid()
功能:获取父进程的进程号
返回值 : 返回进程号

import os

pid = os.fork()

if pid < 0:
print(“Create Process failed”)
elif pid == 0:
#获取当前进程进程号
print(“子进程PID”,os.getpid())
#获取父进程进程号
print(“parent PID”,os.getppid())
else:
print(“父进程PID”,os.getpid())
print(“child PID”,pid) #子进程的PID

#父进程PID 13267
#child PID 13268
#子进程PID 13268
#parent PID 13267

进程的退出
os._exit(status)
功能 : 退出进程
参数 : 进程的退出状态 整数

sys.exit([status])
功能: 退出进程
参数: 不写默认为0
传入一个整数表示退出状态
传入一个字符串,则在进程退出时会打印该字符串

  • sys.exit 可以通过捕获 SystemExit 异常阻止其退出

import os
import sys

#进程结束

os._exit(0)

try:
sys.exit(1)
except SystemExit as e:
# e为异常退出状态
print(e)

print(“Process end”)

作业 :

写一个聊天室

功能 : 类似QQ群聊

  1. 进入聊天室前需要输入用户名
  2. 有人进入聊天室会向其他用户发起通知
    xxx 进入了聊天室
  3. 一个人发消息,其他人都能收到
    xxx 说:xxxxxxxx
  4. 某个人退出聊天室 其他人也会收到通知
    xxx 退出了聊天室

服务端 客户端

  • 使用什么技术
  • 每个功能的实现方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值