网络编程
交互方式
基于同一台机器上的多个程序之间通信,可基于文件
基于多台机器之间的通信,可基于网络
Web程序的两种构架
C/S Client(客户端) Server(服务端)
B/S Browser(浏览器) Server(服务端)
浏览器本身也是客户端的一种
Mac地址
唯一标识一台机器 12位16进制
协议
IP协议: IP地址的规范
IPv4协议 32位二进制4点分十进制
IPv6协议 6冒分16进制组成
交换机不识别IP地址,因此会一对所有广播,之后一对一单播
交换机会完成:地址解析协议 arp协议
通过IP地址找到一台机器的Mac地址
交换机的广播与单播功能
rap协议:通过IP地址找到Mac地址
局域网概念
路由器:一个区域内的多台机器组成的一个网络
域名:和IP地址有对应关系,我们访问的域名经过解析也能得到一个IP地址
网关IP概念
IP地址另一个作用:区分区域网所在的IP范围
在一台机器访问局域网外时使用的出口IP
IP地址
比Mac简易,随机不固定,可按照区域划分
IP地址+Mac地址确认机器
127.0.0.1 本地的回环地址
0.0.0.0 全网段地址
端口
确认本机器上的具体应用
帮助我们查找机器上的对应服务
范围:0-65535
惯用端口:80,8000之后
子网掩码
以相与来确认IP地址的所在范围
socket
所有的网络通信的底层均是基于socket做的
可使用socket模块来实现
import socket
sk=socket.socket()#创建一个socket对象
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1
)
# 表示允许端口复用
sk.bind(('127.0.0.1',10086))#绑定一个服务端地址,必须为元组(ip,端口) sk.listen()#开始接受客户端给我的连接 conn,addr=sk.accept()#阻塞 conn.send(b'welcome 10086')#给连我的人发消息 msg=conn.recv(1024)#阻塞:直到从连我的人那里就收消息 print(msg)#打印消息 conn.close()#关闭 挂电话 sk.close()#关闭 关机
而与之对应的
import socket
sk=socket.socket() sk.connect(('127.0.0.1',10086)) msg=sk.recv(1024) print(msg) txt=input('>>>') sk.send(txt.encode('utf-8')) sk.close()
TCP协议
面向连接
两台机器之间想要传递信息必须先建立连接
之后在有了连接的基础上,进行信息的传递
可靠
数据不会丢失或被重复接收
传递信息后会等待一段时间,若未收到反馈则视为数据丢失,并再次发送
慢
TCP协议需要实现三次握手方可连接 (建立全双工的通信协议)
传递信息后会等待一段时间,若未收到反馈则视为数据丢失,并再次发送
且必须四次挥手断开连接
常用于传递大型数据,例如文件,邮件,亦或是远程操控等
UDP协议
无连接
机器之间传递信息不需要建立连接 直接发送即可
不可靠
数据可能会丢失
快
只负责单向的数据传递
UDP协议常用于即时通讯类软件 如QQ 微信 飞信 歪歪等
互联网协议与osi模型
互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
osi
应用层
应用程序
表示层
会话层
传输层
端口 TCP/UDP协议 四层交换机 四层的路由器
网络层
IP地址相关 IP协议 路由器 三层交换机
数据链路层
mac地址相关 arp协议 网卡 二层交换机
物理层
中继器 集线器 双绞线
UDP
发送端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) #type=socket.SOCK_DGRAM 设置使用UDP协议 sk.bind(('127.0.0.1',10087)) #地址及端口 conn,addr=sk.recvfrom(1024) #recvfrom接受信息 与发送信息者传来的IP地址及端口号 sk.sendto(b'welcome',addr) #sendto 发送信息 至指定的 IP地址的端口 print(conn.decode('utf8')) sk.close()
接收返回端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) conn=('127.0.0.1',10087) #这里写入 自己IP地址 及 端口号 sk.sendto(b'xxx',conn) #sendto发送信息 及 自身IP地址得端口号 msg,_=sk.recvfrom(1024) #recvfrom 接收信息 print(msg.decode('utf8')) sk.close()
粘包
粘包解决方式
本次行动接收者server服务端
import socket import struct sk=socket.socket() sk.bind(('127.0.0.1',10089)) sk.listen() conn,addr=sk.accept() num=conn.recv(4) num=struct.unpack('i',num)[0] msg=conn.recv(num) print(msg.decode('utf-8')) sk.close()
本次行动发送者client客户端
import socket import struct server=socket.socket() server.bind(('127.0.0.1',10086)) server.listen() conn,addr=server.accept() ret=conn.recv(4) long=struct.unpack('i',ret)[0] print(long) msg=conn.recv(long) print(msg.decode('utf8')) conn.recv(1024)
第二种socket套接字创建方式
socketserver 模块
socket模块是socketserver模块的底层模块,socketserver模块是基于socket模块创建的
自带并行 指多个任务于多个cpu上同时进行
import socketserver #导入socketserver模块 import os import hmac #关于加密算法的内置模块/使用的是md5加密算法 class Myserver(socketserver.BaseRequestHandler): def handle(self): #socketserver模块中固定的方法名,由父类的__init__方法调用 key_client = 'my server' #键 conn=self.request #获取自己IP地址 及 端口号 txt=os.urandom(32) #随机生成一个字节串 conn.send(txt) txt=hmac.new(key=key_client.encode(),msg=txt).hexdigest().encode('utf-8') # key,msg均为字节,不可加入字符串等,输出为内存地址,hexdigest()转为字符串 msg=conn.recv(1024) if txt==msg: print('合法') else: conn.close() print('已执行') obj=socketserver.ThreadingTCPServer(('127.0.0.1',10086),Myserver) # 实例化 ((地址及端口),类名) ps:类名是可更改的,仅handle方法名固定 obj.serve_forever() # 调用socketserver模块中的serve_forever()方法
进程
特别注意:子进程任务中若使用 input 函数会报 EOF 的错
进程的创建
开机启动的程序
在运行中再开启一个进程
进程的结束
正常退出
出错退出(资源)
严重出错(非自愿)
被其他进程杀死
多级反馈队列
多个阶层 1→∞ 优先级降序
sys.argv
生成一个索引0为此py文件绝对路径的列表
使用模块操作进程
import multiprocessing 导入一个多元化的进程模块
multiple 多元化 processing 进程
Process 进程类
正常实现一个调用多次函数的代码
import os import time def func(i): time.sleep(1) print(i, os.getpid()) print('主:',os.getpid()) func(0) func(1) func(2)
结果会被按顺序,一个一个的打印出来
这样很稳定,但也很浪费时间
如果我们尝试着加入进程
from multiprocessing import Process 导入multiprocessing模块中的Process方法
from multiprocessing import Process # multiprocessing是个包 # Process进程类 def func(a,b,c): time.sleep(1) print(a,b,c, os.getpid()) if __name__=='__main__': # p=Process(target=func) # p.start() print('主:',os.getpid()) p=Process(target=func,args=(1,2,3)) p.start() #开启一个进程 异步非阻塞模型 高效率 print(p.is_alive()) #判断进程是否存活 print(p.name) #进程名字 print(p.pid) #pid # Process(target=func,args={1:2,2:3,3:4}).start() # Process(target=func,args=[1,2,5]).start() p.terminate() #结束一个进程 异步非阻塞模型 高效率 print(p.is_alive()) time.sleep(0.1) print(p.is_alive()) # 进程之间数据隔离 子进程中返回的值父进程获取不到 # 上例为异步 start()为开启进程 target=为指定目标函数
# multiprocessing是个包
join可以实现进程的阻塞
import random def send_mail(name): time.sleep(random.uniform(1,3)) print('已经给%s发送邮件完毕'%name) if __name__=='__main__': lst=['alex','yuan'] p_l=[] for name in lst: # 阻塞等待一个子进程进程结束 p=Process(target=send_mail,args=[name,]) p.start() #异步非阻塞 p_l.append(p) # p.join() #阻塞 直到p对应的进程结束之后才结束阻塞 print(p_l) i=0 for p in p_l: i+=1 print(i) p.join() print(i) print('所有信息发送完毕') def func(): for i in range(20): time.sleep(0.5) print('in func') if __name__=='__main__': p=Process(target=func) p.daemon=True #设置守护进程 p.start() print('in main') time.sleep(3) print('finished')
主进程与子进程互不干扰
主进程执行完毕之后程序不会结束,会等待所有的子进程结束之后才结束
为什么主进程要等待子进程结束之后才结束
因为主进程要负责给子进程回收一些系统资源
守护进程:
是一个子进程,守护的是主进程
结束条件:主进程的代码结束,守护进程结束
守护进程也是个子进程,需要被主进程回收资源
进程
主进程的代码结束,守护进程结束
主进程要回收守护进程(子进程)的资源
主进程等待其他所有子进程结束
主进程回收所以子进程的资源