网络编程 用来实现不同计算机上运行的程序间进行数据交互 主要应用于文件传输、设备数据传输
IP/端口号/协议 IP确定设备,端口号确定服务,协议保障数据传输的规则
Soket与TCP开发流程 TCP通信:三次握手建立连接,四次挥手断开连接 建立连接后进行数据传输,该协议数据传输可靠Socket 是进程之间通信的一个工具
1 2 3 4 5 6 7 8 9 10 import socket""" 上述创建一个socket对象 参数AddressFamily(地址族)默认选择AF_INET(ipv4),AF_INT6(ipv6)用于进程间通信, Type表示套接字类型(默认SOCK_STREAM,用于TCP协议;SOCK_DGRAM用于UDP) """ 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 mpimport timedef 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 import multiprocessing as mpimport osimport timedef 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())
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)实现多任务的手段