python网络/并发编程部分简单整理

摘要:
有两种类型的套接字,基于文件的和基于网络的。”)Ret=sk。recv#Dialog printsk Close()#关闭客户端套接字的基于UDP的套接字UDP。服务启动后,可以直接接收消息,而无需事先建立链接。服务器端:importSocketudp_Sk=socket。socket#创建服务器的套接字udp_Sk。bind#绑定服务器套接字msg,addr=udp_sk。recvfromprintudp_Sk。sendto#对话udp_Sk。close()#关闭服务器套接字的客户端:importsocketIP_port=udp_sk=socket。套接字_ sk。sendtoback_msg,addr=udp_sk。Recvfromptrint数据包粘连现象:在同时执行多个命令后,只可能获得部分结果。当执行其他命令时,接收先前执行的结果的另一部分。这种现象被称为包粘连。
软件开发架构
C/S架构:Client与Server客户端与服务器端架构
.exe
B/S架构:Browser与Server浏览器端与服务器端架构

IP地址:
IP地址是指互联网协议地址
IP地址通常用“点分十进制”表示,实际上是32位二进制数,通常被分割为4个“8位二进制数”(也就是4个字节)

port:
设备与外界通讯交流的出口。

ip地址精确到具体的一台电脑,端口精确到具体的程序。

osi七层模型

python网络/并发编程部分简单整理第1张

 


socket概念
socket层:

python网络/并发编程部分简单整理第2张

 


socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口
微观角度上看:socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。


套接字
套接字有两种,分别是基于文件型的和基于网络型的。
基于文件类型的套接字
AF_UNIX
unix一切皆文件

基于网络类型的套接字
AF_INET
AF_INET6 --- ipv6


tcp协议和udp协议
TCP:可靠的、面向连接、传输效率低,全双工通信、面向字节流。
使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

UDP:不可靠的、无连接的服务,传输效率高,一对一、一对多、多对一、多对多、面向报文,无拥塞控制。
使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

python网络/并发编程部分简单整理第3张

 



基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

server端:
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)

client端:
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字


基于UDP协议的socket
udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接

server端:
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字

client端:
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)


黏包现象:
同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。
TCP有粘包,UDP无粘包


黏包成因
tcp协议的拆包机制、面向流的通信特点和Nagle算法导致出现黏包

为什么UDP不会发生黏包
UDP 无连接的,面向消息的,提供高效率服务
不会使用块的合并优化算法
采用了链式结构记录每一个到达的UDP包
udp是基于数据报
udp的recvfrom是阻塞的

tcp与udp一次发送数据长度限制
udp:sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节
tcp:不存在长度限制,过长会分段发送


会发生黏包的两种情况
一、发送方的缓存机制
发送端需要等缓冲区满才发送出去,造成粘包
二、接收方的缓存机制
接收方不及时接收缓冲区的包,造成多个包接收

黏包现象只发生在tcp协议中
其本质是因为:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的


黏包的解决方案
方案一:
让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
弊端:程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

方案二:
struct模块
struct模块可以把一个类型,如数字,转成固定长度的bytes

python网络/并发编程部分简单整理第4张

 



socket的更多方法
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据
s.sendall() 发送TCP数据
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字

面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件


send和sendall方法
send()的返回值是发送的字节数量,这个数量值可能小于要发送的string的字节数,
也就是说可能无法发送string中所有的数据。如果有错误则会抛出异常。

sendall()尝试发送string的所有数据,成功则返回None,失败则抛出异常。

socketserver
server端:
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
self.request.sendall(self.data.upper())

if __name__ == "__main__":
HOST, PORT = "127.0.0.1", 9999

# 设置allow_reuse_address允许服务器重用地址
socketserver.TCPServer.allow_reuse_address = True
# 创建一个server, 将服务地址绑定到127.0.0.1:9999
server = socketserver.TCPServer((HOST, PORT),Myserver)
# 让server永远运行下去,除非强制停止程序
server.serve_forever()

client端:
import socket

HOST, PORT = "127.0.0.1", 9999
data = "hello"

# 创建一个socket链接,SOCK_STREAM代表使用TCP协议
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT)) # 链接到客户端
sock.sendall(bytes(data + " ", "utf-8")) # 向服务端发送数据
received = str(sock.recv(1024), "utf-8")# 从服务端接收数据

print("Sent: {}".format(data))
print("Received: {}".format(received))



并发编程
操作系统介绍:
批处理:联机批处理、脱机批处理
单道:

python网络/并发编程部分简单整理第5张

 


多道:

python网络/并发编程部分简单整理第6张

 


多道程序系统的出现,标志着操作系统渐趋成熟的阶段,先后出现了作业调度管理、处理机管理、存储器管理、外部设备管理、文件系统管理等功能。
由于多个程序同时在计算机中运行,开始有了空间隔离的概念,只有内存空间的隔离,才能让数据更加安全、稳定。
出了空间隔离之外,多道技术还第一次体现了时空复用的特点,遇到IO操作就切换程序,使得cpu的利用率提高了,计算机的工作效率也随之提高。


分时:

python网络/并发编程部分简单整理第7张

 


把处理机的运行时间分成很短的时间片,按时间片轮流把处理机分配给各联机作业使用。
特点:多路性,交互性,独立性,及时性

主要目标:对用户响应的及时性,即不至于用户等待每一个命令的处理时间过长。

注意:分时系统的分时间片工作,在没有遇到IO操作的时候就用完了自己的时间片被切走了,这样的切换工作其实并没有提高cpu的效率,反而使得计算机的效率降低了。但是我们牺牲了一点效率,却实现了多个程序共同执行的效果,这样你就可以在计算机上一边听音乐一边聊qq了。


实时:
实时系统可分成两类:
实时控制系统:发射导弹
实时信息处理系统:预定飞机票等
特点:
及时响应
高可靠性


通用:
操作系统的三种基本类型:多道批处理系统、分时系统、实时系统。
通用操作系统:具有多种类型操作特征的操作系统。可以同时兼有多道批处理、分时、实时处理的功能,或其中两种以上的功能。


操作系统的进一步发展:
个人计算机操作系统-----网络操作系统-----分布式操作系统

操作系统的作用
程序员无法把所有的硬件操作细节都了解到,管理这些硬件并且加以优化使用是非常繁琐的工作,这个繁琐的工作就是操作系统来干的,有了他,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件的编写就可以了,应用软件直接使用操作系统提供的功能来间接使用硬件。
精简的说的话,操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序。操作系统所处的位置如图

一:隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,更简单,更清晰的模型(系统调用接口)。应用程序员有了这些接口后,就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
二:将应用程序对硬件资源的竞态请求变得有序化

进程:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
狭义定义:进程是正在运行的程序的实例
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

进程的概念:
一、进程是一个实体
二、进程是一个“执行中的程序”

操作系统引入进程的概念的原因
从理论角度看,是对正在运行的程序过程的抽象;
从实现角度看,是一种数据结构,目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。

进程的特征
动态性、并发性、独立性、异步性
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序

进程与程序中的区别
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
程序是永久的,进程是暂时的。

进程调度


进程的并行与并发
并行 : 并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的CPU )
并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。
区别:
并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。


同步异步阻塞非阻塞
三状态:就绪,运行和阻塞。

python网络/并发编程部分简单整理第8张

 


同步和异步
同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成
异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了

阻塞与非阻塞
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)
时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的

同步/异步与阻塞/非阻塞
同步阻塞形式-------效率最低

异步阻塞形式----------异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。

同步非阻塞形式--------实际上是效率低下的

异步非阻塞形式---------效率高

进程的创建与结束
进程的创建:
1. 系统初始化
2. 一个进程在运行过程中开启了子进程
3. 用户的交互式请求,而创建一个新进程
4. 一个批处理作业的初始化
无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。 

进程的结束
1. 正常退出
2. 出错退出
3. 严重错误
4. 被其他进程杀死



在python程序中的进程操作
multiprocess模块
multiprocess不是一个模块而是python中一个操作、管理进程的包


multiprocess.process模块
|
|
|----process模块介绍
|
|
|----使用process模块创建进程
|
|
|----守护进程
|
|
|----多进程中的其他方法
| |
| |
| |----terminate
| |
| |
| | ----is_alive
|----进程对象的其他属性
|
|
|----pid
|
|
| ----name

进程同步
锁 —— multiprocess.Lock
信号量 —— multiprocess.Semaphore
事件 —— multiprocess.Event

进程间通信——队列和管道

进程间通信:IPC(Inter-Process Communication)

队列:Queue

生产者消费者模型
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

管道

进程之间的数据共享
进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题


进程池和multiprocess.Pool模块



线程
进程是资源分配的最小单位,线程是CPU调度的最小单位,每一个进程中至少有一个线程。 

线程与进程的区别
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程操作系统中,进程不是一个可执行的实体。


线程的特点:
轻型实体、独立调度和分派的基本单位、共享进程资源、可并发执行

线程和python
全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

python线程模块
threading模块----Threading.Thread

守护线程
无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行
1.对主进程来说,运行完毕指的是主进程代码运行完毕
2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕


同步锁、死锁与递归锁

信号量

事件

定时器

线程队列
class queue.Queue(maxsize=0) # 先进先出
class queue.LifoQueue(maxsize=0) # last in fisrt out # 后进先出
class queue.PriorityQueue(maxsize=0) # 存储数据时可设置优先级的队列

Python标准模块--concurrent.futures



协程
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

对比操作系统控制线程的切换,用户在单线程内控制协程的切换
优点:
1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点:
1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

特点:
必须在只有一个单线程里实现并发
修改共享数据不需加锁
用户程序里自己保存多个控制流的上下文栈
附加:一个协程遇到IO操作自动切换到其它协程

Greenlet模块
Gevent模块

Gevent之同步与异步



I/O多路复用
IO模型介绍:
blocking IO 阻塞IO
nonblocking IO 非阻塞IO
IO multiplexing IO多路复用
signal driven IO 信号驱动IO
asynchronous IO 异步IO

阻塞IO-----blocking IO
blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

非阻塞IO-----non-blocking IO
在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有。但是非阻塞IO模型绝不被推荐

多路复用IO------IO multiplexing
1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
结论: select的优势在于可以处理多个连接,不适用于单个连接

异步IO-------Asynchronous I/O


selectors模块


免责声明:文章转载自《python网络/并发编程部分简单整理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Asp.NET导出Excel文件乱码解决若干方法 (转)Windows使用cmd命令行中查看、修改、删除与添加环境变量下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

UNIX网络编程——send与recv函数详解

#include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags); send和recv的前3个...

Python使用grequests并发发送请求

目录 前言 grequests简单使用 grequests和requests性能对比 异常处理 前言 requests是Python发送接口请求非常好用的一个三方库,由K神编写,简单,方便上手快。但是requests发送请求是串行的,即阻塞的。发送完一条请求才能发送另一条请求。 为了提升测试效率,一般我们需要并行发送请求。这里可以使用多线程,或者...

转:Delphi中使用比较少的一些语法

http://www.cnblogs.com/Murphieston/p/5577836.html本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要。 ---Murphy 1,构造和析构函数: a,构造函数: 一般基于TComponent组件的派生类,都应该使用overload关键字进行继承,Delp...

python下载安装requests库

一、python下载安装requests库 1、到git下载源码zip源码https://github.com/requests/requests 2、解压到python目录下: 3、“win+R”进入cmd;依次输入如下代码: C:UsersAdministrator>cd D:softwareITPython27 equests-maste...

YAML书写规则与数据结构

YAML 是一种简洁的非标记语言。YAML以数据为中心,使用空白,缩进,分行组织数据,从而使得表示更加简洁易读。 基本规则 YAML有以下基本规则:1、大小写敏感2、使用缩进表示层级关系3、禁止使用tab缩进,只能使用空格键4、缩进长度没有限制,只要元素对齐就表示这些元素属于一个层级。5、使用#表示注释6、字符串可以不用引号标注 三种数据结构 1、ma...

python文件和目录操作方法大全

一、python中对文件、文件夹操作时经常用到的os模块和shutil模块常用方法。1.得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()2.返回指定目录下的所有文件和目录名:os.listdir()3.函数用来删除一个文件:os.remove()4.删除多个目录:os.removedirs(r“c:python”)5.检验给...