#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @IDE : PyCharm
# @Project :mytest
# @File : day30.py
# @Time : 2019/9/2 8:04
# @Author : wentao song
''''''
'''
作业:
1. QQ聊天:使用Queue,Pipe,作为数据的载体,通过Process
from multiprocessing import Process,Pipe
import time,os
# 通讯方向:
# 单工、单项通信:收端只能听,发端只能发
# 双工、通信两端,都可以进行随时的信息的发送与接收
# 半双工:通信两端遵循一定的协议实现两端数据的双向通讯
# 对讲机:P1,P2:调整频率相同,设定P1发送,P2接收 P1------>P2
# 设置P1接收,P2发送 P1<---------P2
def user1(ms):
while True:
# send方法发送的信息放在一个参数中
ms.send(str(time.asctime())+str(os.getpid()))
time.sleep(0.5)
def user2(ms):
while True:
print(ms.recv())
def mainFunc():
#完成进程、管道创建,运行进程
MS,MR = Pipe()
P1 = Process(target=user1,args=(MS,))
P2 = Process(target=user2,args=(MR,))
P1.start()
P2.start()
if __name__ == '__main__':
mainFunc()
------------------------------------------------
2.性能对比:传统方式、进程、线程
a. CPU性能,计算150万次算术运算
b. 文件读写效率,完成对一个文件的500万次数据的写入和读取
---------------------------------
from multiprocessing import Process,Pool
from threading import Thread
import time
def CalFunction():
#150万次运算:
x,y,z = 1,1,1
while x<500000:
x+=1
y+=1
z+=1
def WriteFunction():
with open('./text.txt',mode='w') as f:
for x in range(500000):
f.write('write test!')
def ReadFunction():
with open('./text.txt', mode='r') as f:
for x in range(500000):
f.readline()
def IOFunction():
WriteFunction()
ReadFunction()
#常规:
def CalCommon():
start_time= time.time()
for i in range(10):
CalFunction()
end_time = time.time()
print(end_time-start_time)
def IOCommon():
start_time = time.time()
IOFunction()
end_time = time.time()
print(end_time - start_time)
def CalThread():
start_time = time.time()
threads = []
for i in range(10):
T = Thread(target=CalFunction)
threads.append(T)
T.start()
for i in threads:
i.join()
end_time = time.time()
print(end_time-start_time)
def IOThread():
start_time = time.time()
threads = []
for i in range(10):
T = Thread(target=IOFunction)
threads.append(T)
T.start()
for i in threads:
i.join()
end_time = time.time()
print(end_time-start_time)
def CalPool():
start_time = time.time()
P = Pool(5)
for i in range(10):
P.apply_async(CalFunction)
P.close()
P.join()
end_time = time.time()
print(end_time-start_time)
def IOPool():
start_time = time.time()
P = Pool(5)
for i in range(10):
P.apply_async(IOFunction)
P.close()
P.join()
end_time = time.time()
print(end_time-start_time)
if __name__ == '__main__':
# CalCommon()#0.6566615104675293
# IOCommon()#21.214055061340332
# CalThread()#0.6696805953979492
# IOThread() #46.42314839363098
# CalPool()#0.5188260078430176
IOPool()#11.366146087646484
---------------------------------
3. turtle模块实现科赫曲线:
需要绘制长度为X的科赫曲线需要这么做:
a.绘制长度X/3科
b.向左转60°
c.绘制长度X/3科赫曲线
d.向右转120°
e.绘制长度X/3科赫曲线
f.向左转60°
g.绘制长度X/3科赫曲线
import turtle
def koch(T,n):
if n<10:
T.fd(n)
return
m = n/3
koch(T, m)
T.lt(60)
koch(T, m)
T.rt(120)
koch(T, m)
T.lt(60)
koch(T, m)
def Snow(t,n):
for i in range(3):
koch(t,n)
t.rt(120)
def mainFunc():
T = turtle.Turtle()
Snow(T,300)
turtle.mainloop()
if __name__ == '__main__':
mainFunc()
------------------------------------
4.现有双向链表:{2,3,1,0,2,5,3},实现链表去重
class Node():
def __init__(self, dataval, preval = None, nextval = None):
self.dataval = dataval
self.preval = preval
self.nextval = nextval
# 链表操作:删除前需:创建、添加、求长度等操作
class Dlinklist():
def __init__(self):
self.header = None
def listNode(self):
nodes = []
cur = self.header
while cur != None:
nodes.append(cur.dataval)
cur = cur.nextval
return nodes
def getLength(self):
cur = self.header
length = 0
while cur != None:
length+=1
cur=cur.nextval
return length
def headerInsert(self, newdata):
NewNode = Node(newdata)
if self.header == None:
self.header = NewNode
else:
cur = self.header
self.header = NewNode
NewNode.nextval = cur
cur.preval = NewNode
def endInsert(self, newdata):
NewNode = Node(newdata)
if self.header == None:
self.header = NewNode
else:
cur = self.header
while cur.nextval !=None:
cur = cur.nextval
cur.nextval = NewNode
NewNode.preval = cur
def indexInsert(self, newdata, index):
if index == 1:
self.headerInsert(newdata)
elif index > self.getLength():
self.endInsert(newdata)
else:
cur = self.header
NewNode = Node(newdata)
for i in range(index):
cur = cur.nextval
second = cur.nextval # 断点后的节点,新插入节点位于second前,cur后
cur.nextval = NewNode
NewNode.preval = cur
NewNode.nextval = second
second.preval = NewNode
def distinctNode(self):
orgList = self.listNode()
distinctList = []
self.header = None
for i in orgList:
if i not in distinctList:
distinctList.append(i)
self.endInsert(i)
# def distinctNode(self):# 节点顺序排列问题
# orgList = self.listNode()
# distinctList = list(set(orgList))
# self.header = None
# for i in distinctList:
# self.endInsert(i)
#2,3,1,0,2,5,3
if __name__ == '__main__':
dList = Dlinklist()
dList.indexInsert(2,1)
dList.indexInsert(3, 2)
dList.indexInsert(1, 3)
dList.indexInsert(0, 4)
dList.indexInsert(2, 5)
dList.indexInsert(5, 6)
dList.indexInsert(3, 7)
print(dList.listNode())
dList.distinctNode()
print(dList.listNode())
----------------------------------------------------
2. 线程
加锁:Lock(), acquire加锁,release解锁
from threading import Thread, Lock
import os, time
# 为子线程加锁,原始的并行改为串行执行,牺牲效率保证数据安全性
# 为运行的线程添加编号:n,运行时打印
def SubFunction(n,l):
l.acquire()
print('SubFunction is Running, PID:%s, number:%s'%(os.getpid(),n))
time.sleep(0.1)
print('SubFunction End, PID:%s, number:%s'%(os.getpid(),n))
l.release()
def MainFunc():
threads = []
lock = Lock()
for i in range(100):
T = Thread(target=SubFunction, args=(i,lock))
threads.append(T)
T.start()
# T.join()
for i in threads:
i.join()
if __name__ == '__main__':
MainFunc()
-------------------------------------------------
死锁:加锁能够让外部任务处于等待状态。
如果同时存在两个以上的进程或者线程同时加锁,就会导致因为加锁抢占资源
而导致的进程之间相互等待的现象。死锁现象会导致程序无法继续向下执行。
-----------------------------------------
from threading import Thread, Lock
import time
lock1 = Lock()
lock2 = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
lock1.acquire()
print('%s 拿到lock1锁'%self.name)
lock2.acquire()
print('%s 拿到lock2锁'%self.name)
lock2.release()
lock1.release()
def func2(self):
lock2.acquire()
print('%s 拿到lock2锁'%self.name)
time.sleep(2)
lock1.acquire()
print('%s 拿到lock1锁'%self.name)
lock1.release()
lock2.release()
if __name__ == '__main__':
for i in range(10):
T = MyThread()
T.start()
------------------------------------------------------
递归锁:在python中为了支持在同一个线程中多次申请同意资源,加入RLock
RLock内部存在Lock和计数器,计数器记录acquire次数,从而资源可以被多次
acquire,直到一个线程所有acquire都被release,其他进程才能够获取资源
-------------------------------------
from threading import Thread, RLock
import time
lock1 = lock2 = RLock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
lock1.acquire()
print('%s 拿到lock1锁' % self.name)
lock2.acquire()
print('%s 拿到lock2锁' % self.name)
lock2.release()
lock1.release()
def func2(self):
lock2.acquire()
print('%s 拿到lock2锁' % self.name)
time.sleep(2)
lock1.acquire()
print('%s 拿到lock1锁' % self.name)
lock1.release()
lock2.release()
if __name__ == '__main__':
for i in range(10):
T = MyThread()
T.start()
------------------------------------------------
信号量:Semaphore
与Lock一样,信号量也是一把锁,信号量可以设置数值,设定信号量为N
在同一时间内,可以有N个任务一起执行
from threading import Thread, Semaphore
import threading,time,os
def func():
sp.acquire()
print('%s runngint,PID:%s'%(threading.current_thread().getName(), os.getpid()))
time.sleep(3)
print('%s end,PID:%s' % (threading.current_thread().getName(), os.getpid()))
sp.release()
if __name__ == '__main__':
sp = Semaphore(5)
for i in range(20):
T = Thread(target=func)
T.start()
Semaphore:管理者一个内置的计数器,每当调用acquire时,内置计数器-1
调用release时,内置计数器+1
计数器不能小于0,当计数器为0,acquire将会阻塞其他进程调用release
----------------------------------------------
计时器:Timer,可以指定任务在预设时间后运行
from threading import Timer
# 自定义函数
def hello():
print('Hello Timer!')
# 实例化Timer对象,传入两个参数,参数1:延时时间,参数2,执行对象
T = Timer(3, hello)
# 执行定时器
T.start()
----------------------------------------------
线程的队列:Queue
用法与进程相同,可以用在多个线程之间进行信息交换
# 1.先进先出队列
from queue import Queue
q = Queue()
for i in range(10):
q.put(i)
for i in range(10):
print(q.get(), end=',')
print()
# 2.LifoQueue
from queue import LifoQueue
LQ = LifoQueue()
for i in range(10):
LQ.put(i)
for i in range(10):
print(LQ.get(), end=',')
print()
# 3. PriorityQueue,具备设置优先级选项的队列
from queue import PriorityQueue
PQ = PriorityQueue()
# put方法传入数据,数据类型是元组,元组的第一个参数是优先级
# (通常是数字,也可以进行非数字比较),数字越小,优先级越高
PQ.put((20,'a'))
PQ.put((10,'b'))
PQ.put((30,'c'))
# 优先级高的成员先出队
print(PQ.get())
print(PQ.get())
print(PQ.get())
'''
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @IDE : PyCharm
# @Project :mytest
# @File : day31.py
# @Time : 2019/9/3 8:01
# @Author : wentao song
''''''
'''
1. 线程:
多线程
线程队列
锁:死锁、递归锁、信号量
比较进程、线程在计算和IO读写:
运算:进程快,线程慢
IO: 线程快,进程慢
进程与线程的应用:
多线程:多应用在IO密集型操作,比如socket通信、爬虫、web开发
多进程:多应用与计算密集型操作,比如机器学习,金融分析等
代码:运算
# 1. 导入库和模块
from multiprocessing import Process
from threading import Thread
import time, os
#执行运算的函数,提供给子进程或者子线程调用
def work():
data = 0
for i in range(100000000):
data*=i
# 在主进程中,创建子进程或者子线程
# 执行多任务并发执行
def mainFunc():
# 接收需要执行并发任务的进程或者线程
events = []
# 查看计算机基本信息,显示计算机CPU核数
print(os.cpu_count())
start_time = time.time()
#启动多个进程或者线程
for i in range(4):
# P = Process(target=work) # run time is 7.812999248504639
P = Thread(target=work) #run time is 20.479247570037842
events.append(P)
P.start()
for i in events:
i.join()
end_time = time.time()
print('run time is %s'%(end_time-start_time))
if __name__ == '__main__':
mainFunc()
-------------------------------------------------------
#IO运算
from threading import Thread
from multiprocessing import Process
import os,time
#函数,被子进程或者子线程调用
def work():
time.sleep(2)
print('------>')
def mainFunc():
events = []
print(os.cpu_count())
start_time = time.time()
for i in range(400):
# T = Thread(target=work) #run time is 2.0476455688476562
T = Process(target=work) #run time is 15.327229976654053
events.append(T)
T.start()
for i in events:
i.join()
end_time = time.time()
print('run time is %s'%(end_time-start_time))
if __name__ == '__main__':
mainFunc()
2. 事件对象:Event
线程中提供的方法:用来控制线程执行状态
原因:多个线程在一个进程中执行。线程状态未知,存在不可预测的风险。
Event:事物处理对象管理线程,内置方法有,
is_set(), set(), clear(),wait()
is_set() 设置线程内部标志为True时,返回False
set() 将进程内部标志设置为Ture,此时所有等待它为Ture的线程会被唤醒
线程调用wait()方法时,不会阻塞
clear() 将进程内部标志设置为False,随后调用wait()将进程阻塞
wait(timeout=None) 执行阻塞,如果线程内部标志为Ture立即返回,
否则执行阻塞
--------------------------------------------------------
练习:
# 需求:使用线程实现生产者、消费者模型
# 1. 以继承方式实现线程创建,线程之间数据交换通过消息队列
# 2. 两个线程设置,生产者为守护线程: 守护线程负责生产包子,消费者线程负责买包子
# 3. 买包子需要钱,一个包子一块钱,一共七块钱,如果没钱了,停止购买
# 4. 设置消息队列长度为5
-----------------------------------------------------
import threading, queue, time
class Producer(threading.Thread):
# 重写run方法
def run(self):
# 标记包子数量
global num
while True:
#判断队列为空
if goods.empty():
#阻塞,生产包子
event.clear()
for i in range(5):
goods.put('包子-'+str(num))
print('生产了包子-{0}.'.format(str(num)))
num+=1
time.sleep(0.5)
event.set()
class Consumer(threading.Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#增加属性,7元钱
self.monkey = 7
def run(self):
while self.monkey:
#设置线程阻塞
event.wait()
self.monkey-=1
print('{0}买了一个{1}。'\
.format(threading.current_thread().getName(), goods.get()))
time.sleep(1)
print('{0}没钱了,回家。'.format(threading.current_thread().name))
def mainFunc():
P = Producer(daemon=True)
#在继承的类中传入人名
C1 = Consumer(name='关一峰')
C2 = Consumer(name='周涛')
P.start()
C1.start()
C2.start()
C1.join()
C2.join()
if __name__ == '__main__':
# 引入Event事件
event = threading.Event()
# 引入队列,作为数据交换载体
goods = queue.Queue(5)
# 计数器
num = 0
mainFunc()
---------------------------------------------------------
进程池与线程池:
池:声明创建进程或者线程的个数,通过控制创建数量,在节省系统资源的
前提下,提升代码执行效率
concurrent.futures: 模块,提供高度封装的异步调用接口
ThreadPoolExecutor:线程池,提供异步调用
ProcessPoolExecutor :进程池,提供异步调用
基本方法:
submit():提交异步任务
map(): 取代for循环submit的操作
shutdown():相当于执行 pool.close()+pool.join()
关闭进程或线程池,执行进程或线程
result():获取结果
add_done_callback():回调函数
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import os,time
def work():
print('%s is running'%os.getpid())
time.sleep(2)
print('%s is end!' % os.getpid())
def mainFunc():
# 创建进程池或线程池,max_workers指定创建池子的大小
# Executor = ThreadPoolExecutor(max_workers=3)
Executor = ProcessPoolExecutor(max_workers=3)
for i in range(11):
Executor.submit(work)
Executor.shutdown()
if __name__ == '__main__':
mainFunc()
-----------------------------------------------------
回调函数:
concurrent.futures,回调函数可以为提交执行的进程或线程绑定一个函数
在进程或者线程执行结束,触发这个函数,并且进程或线程接受返回值作为参数
提交任务的方法:
1. 同步调用:提交任务后,原地等待任务执行完毕,拿到的结果继续执行。
同步调用会导致程序串行
2. 异步调用:提交任务之后,无需等待任务执行结束
----------------------------------------------------------
同步调用实例:
from concurrent.futures import ThreadPoolExecutor
import time,random
def EatStatus(name):
print("%s 正在吃包子"%name)
time.sleep(random.randint(1,2))
res = random.randint(1,20)*'#'
return {'name':name,'res':res}
def EatNum(food):
name = food['name']
size = len(food['res'])
print('%s 吃了 %s个包子'%(name, size))
#在线程池中创建线程,线程执行后,调用回调函数add_done_callback
def mainFunc():
pool = ThreadPoolExecutor(13)
# 提交线程任务同时获取线程执行结果,同时使用EatNum进行同步调用
P1 = pool.submit(EatStatus, '关一峰').result()
EatNum(P1)
P2 = pool.submit(EatStatus,'孙一山').result()
EatNum(P2)
P3 = pool.submit(EatStatus, '何立国').result()
EatNum(P3)
if __name__ == '__main__':
mainFunc()
---------------------------------------------------
# 异步调用
from concurrent.futures import ThreadPoolExecutor
import random,time
# 方法不变
def EatStatus(name):
print("%s 正在吃包子"%name)
time.sleep(random.randint(1,2))
res = random.randint(1,20)*'#'
return {'name':name,'res':res}
# 改写吃包子方法
def EatNum(food):
# 异步调用固定写法
food = food.result()
name = food['name']
size = len(food['res'])
print('%s 吃了 %s个包子' % (name, size))
def mainFunc():
pool = ThreadPoolExecutor(13)
# 提交线程任务,EatNum进行异步调用
pool.submit(EatStatus, '关一峰').add_done_callback(EatNum)
pool.submit(EatStatus, '孙一山').add_done_callback(EatNum)
pool.submit(EatStatus, '何立国').add_done_callback(EatNum)
if __name__ == '__main__':
mainFunc()
--------------------------------------------------
map:
from concurrent.futures import ThreadPoolExecutor
#爬虫库
import requests
"""
# 采用requests爬虫库,爬取列表第一项数据
response = requests.get(URLS[0])
#从网站返回的数据对象中打印其文本信息:.text
print(response.text)
"""
def task(url,timeout=10):
return requests.get(url, timeout=timeout)
def mainFunc():
pool = ThreadPoolExecutor(max_workers=3)
results = pool.map(task, URLS)
for result in results:
print(len(result.text))
if __name__ == '__main__':
URLS = ['http://www.baidu.com', 'http://www.qq.com', 'http://www.sina.com']
mainFunc()
3. 协程:
单线程下实现的并发。也称为微线程。协程是一种用户态的轻量级线程,协程是由用户
自己控制调度的。线程是CPU调度的执行单元。
优点:
无需线程上下文切换的开销,程序员自行控制
高并发,高扩展,低成本
缺点:
无法利用多核的资源,协程本质上是一个单线程
在执行阻塞操作时,会阻塞掉协程整个程序
-----------------------------------------------------------
# 生成器:yield,next
# 生成器协程,并发执行func1与func2,效率较低
import time
def func1():
while True:
yield
def func2():
g = func1()
for i in range(100000000):
next(g)
def mainFunc():
start_time = time.time()
func2()
stop_time = time.time()
print(stop_time-start_time)
if __name__ == '__main__':
mainFunc()
'''
1. yeild可以保存状态,yield状态保存与操作系统保存线程很像,
但是yeild是代码级别的控制,更加轻量级
2. send可以把函数执行结果传给另外一个函数,借此实现单线程内部并发运行
def consumer(name):
print('开始吃包子。。。')
while True:
print('consumer %s 需要包子'%name)
baozi = yield #接收producer发送过来的数据
print('%s 吃了 %s个包子'%(name, baozi))
def producer(obj):
obj.send(None) #必须发送None内容到consumer
for i in range(3):
print('producer 正在做%s个包子'%i)
obj.send(i)
if __name__ == '__main__':
person1 = consumer('Alex')
producer(person1)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @IDE : PyCharm
# @Project :mytest
# @File : day32.py
# @Time : 2019/9/4 8:00
# @Author : wentao song
'''
1. 协程
①.生成器, yield\next\send
②.greenlet模块
③.gevent,实现、调用
2. Socket网络通信
①. 网络知识储备
②.CS架构与BS架构
③.socket网络编程
'''
# 使用yield生成器模拟协程:
# 协程:是单线程下实现的并发,又称为为线程。
# 是一种用户级别的轻量级线程,协程是由用户执行调度的。
# import time
#
# def func1():
# while True:
# yield
#
# def func2():
# g = func1()
# for i in range(100000000):
# i+=1
# next(g)
#
# def mainFunc():
# start = time.time()
# func2()
# stop = time.time()
# print(stop-start)
#
# if __name__ == '__main__':
# mainFunc()
# greenlet是一个小型独立伪线程。可以把它想象成栈的成员,栈底是初始调用的函数
# 栈顶是greenlet的暂停位置。使用greenlet创建的不同堆栈可以在暂停状态执行跳转,
# 跳转到另外一个greenlet
# 方法介绍
# 1. greenlet.greenlet是一个数据类型
# 2. greenlet(run=None, parent=None)
# 创建greenlet对象,run是指需要执行的函数,传参可以省略掉run=,
# 如果之传入一个参数,代表要执行执行的函数
# 3. greenlet.getcurrent()
# 返回当前greenlet,也就是那个函数在被执行
# 4. greenlet.GreenletExit
# 特定的异常出现时,不会关联parent指定的函数
# 5. greenlet.switch(),在greenlet对象之间执行跳转
#执行switch跳转时,任务跳转并保留当前位置,下一次执行,从当前位置开始执行
# from greenlet import greenlet
#
# def func1():
# print('Func1 running')
# g2.switch()
# print('Func2 end')
# g2.switch()
#
# def func2():
# print('Func2 running')
# g1.switch()
# print('Func1 end')
#
# if __name__ == '__main__':
# # 通过greenlet 跳转机制,实现func1与func2函数之间跳转
# g1 = greenlet(func1)
# g2 = greenlet(func2)
# g1.switch()
#
# # 参照上述跳转,
# # 实现在两个函数之间的任务往复跳转
# # 函数1 自增一亿次
# # 函数2 自乘一亿次
# gevent实现的协程
# gevent是一个第三方库,可以实现协程调用方式,它是以扩展的C模块形式
# 接入python的轻量级协程,gevent全部运行的主程序都在进程内部,
# 执行协程模式的调度。
# gevent会主动的识别程序内部IO操作,当子程序遇到IO阻塞后,会切换到其他子程序
# 如果所有子程序都进入IO阻塞,则阻塞
# from gevent import monkey
# monkey.patch_all()
# import gevent, time
#
# def eat(name):
# print('%s eatting apple'%name)
# time.sleep(2)
# # gevent.sleep(2)
# print('%s eatting banana'%name)
#
# def play(name):
# print('%s playing basketball'%name)
# time.sleep(3)
# # gevent.sleep(3)
# print('%s playing game'%name)
#
# def mainFunc():
# g1 = gevent.spawn(eat, 'Alex')
# g2 = gevent.spawn(play, 'Alex')
# g1.join()
# g2.join()
# print('mainFunc End!')
#
# if __name__ == '__main__':
# mainFunc()
#1. gevent在单线程并发执行时,效率更高,因为它会在IO阻塞时,执行任务跳转
#2. gevent,能够识别 由 gevent.sleep()造成的IO阻塞。其他类型的阻塞无法识别
#3. 使用如下代码,可以识别所有类型的IO阻塞问题
# from gevent import monkey
# monkey.patch_all()
#--------------------------------------------------------------------
# 网络通信部分:
# 基础知识: PC网络设备,无线、有线网卡,网口
#
# 客户端软件基于网络,发送一条信息给服务端,流程:
# 1. 客户端软件,产生数据,存放在客户端软件的内存中,然后调用接口将自己内存中存储的
# 数据发送或者拷贝给操作系统的内存
#
# 2. 客户端操作系统收到数据后,按照客户端指定的规则(协议),调用网卡发送数据
#
# 3. 网络传输数据
#
# 4. 服务端软件调用系统接口,想要将操作系统内存中的数据拷贝到自己的内存中
#
# 5. 服务端操作系统收到4的指令后,使用与客户端相同的规则(协议)从网卡中将数据拷贝到
# 服务端软件
#
# 规则:高低电平信号的组合方式
# 网线数据:高低电平,高代表1,低代表0,
# 1111 0001 0100 0000
# 11110001 01000000
# 互联网协议:根据划分方式分为三类
# 第一类:OSI七层模型
# 第二类:tcp/ip 五层模型
# 第三类:tcp/ip 四层模型
#
# TCP/IP五层模型:
#
# 物理层:计算机之间想要完成网络通信,就必须接入网络(局域网、广域网),
# 言外之意,计算机必须联网组网
# 网络介质:光缆、电缆、双绞线、无线通讯
#
# 功能: 基于电器特性发送高低电平,高电平1,低电平0
#
# 数据链路层:
# 数据链路层由来:单纯的电信号101010000111毫无意义,必须规定电信号是多少位一组
# 每组是什么含义
# 功能:定义电信号的分组方式
#
# # RS232,485, 422,modbus can spi zigbee wifi
#
# 以太网协议:
# 早期各个公司都有自己的分组方式,为了统一,出现了以太网协议Ethernet
#
# 规定:
# 每组电信号构成一个数据包,也可以称作帧
# 每一帧数据分为:包头header,数据data
#
# EE 4A BD AF 00 01 57 FF 41
# EE 4A BD AF 41 19 64 F1 00
#
# header包含 固定18个字节
# 发送者/源地址:6字节
# 接收者/目的地址:6字节
# 数据类型:6字节
# data:最短46字节,最长1500字节
# data就是数据包中的有效信息
# mac地址:
# 物理地址,是计算机网卡的编号
# 查看方式:ipconfig -all
# 结果
# 物理地址. . . . . . . . . . . . . : 68-F7-28-EE-C4-C6
# 广播:
# 有了mac地址,同一个网络中两台计算机就可以通信了
# 发端发信息到整个网络内的所有计算机 称为广播
#
# 网络层:
# 有了Ethernet,mac地址,广播的通信方式后,世界上的计算机就可以通信了。
# 问题是世界范围内联网是由一个个彼此隔离的小局域网组成的
# 如果采用以太网广播方式传输数据,那么所有网络内的计算机都可以收到信息
# 这种通信效率低,安全性差
#
# 引入一套全新的地址区分广播域(发送广播数据)和子网(发送点对点数据),
# 这套地址称为网址(IPV4/IPV6)
#
# # 网址中每一位最大值是255,最小值是0
# # 192.168.1.x: 256台
# # 192.168.x.x 256**2台
# # 220.x.x.x 256**3台
# ICMP(网络控制报文)协议
# 当传送IP数据包发生错误时,比如:主机不可达、路由不可达等等,ICMP协议
# 就会把错误信息封装数据包返回给主机。比如ICMP协议中的ping命令
#
# 传输层:TCP/UDP
# 网络层IP可以帮助我们区分子网 广播域,以太网可以通过mac地址帮助我们找到主机,
# 应用程序怎么办?在PC上打开QQ 淘宝等应用,如何处理?
#
# 我们需要通过主机上的端口号实现以上要求。
#
# 传输层创建端口的范围:
# 端口表达范围:0~65535,0~1023为系统占用端口
#
# windows下查看端口占用情况
# netstat -ano|findstr "3306"
# 查看PID被哪个服务占用
# tasklist |findstr "23400"
#
# 应用层:
# 用户使用的应用程序位于TCP/IP五层架构的应用层,互联网开发的与处于应用层开发
# 由于应用层软件需要的数据格式都各不相同,因此用用层必须规定好数据的组织形式
# 规定应用程序的数据格式
# TCP协议可以为各种应用程序提供数据,比如说 Email FTP 浏览器等等
#
# 传输层的C\S架构与B\S架构
# 客户端的英文名称:Client
# 浏览器的英文名称:Browser
# 服务器的英文名称:Server
# C\S架构就是 Client\Server架构, B\S架构就是 Browser\Server架构
#
# ①. 硬件C\S架构:电脑与连接电脑的打印机
# ②. 软件C\S架构:QQ 微信 暴风影音 优酷都是C\S架构
# ③. 软件B\S架构:基于浏览器访问的软件,数据在浏览器与浏览器服务器之间传输
#
# Socket实现网络通信:
# 定义: socket是应用层与TCP/IP协议族通信的中间的抽象层,它是一组接口。它把
# 复杂的TCP/IP协议族隐藏起来,为用户只提供接口。Socket只负责组织数据
#
# 分类:
# 1. 基于文件类型的套接字 AF_UNIX
# 2. 基于网络的套接字 AF_INET
#
# 套接字类型:
# socket.SOCK_STREAM TCP
# socket.SOCK_DGRAM UDP
# socket.SOCK_RAW 原始套接字,
# 当普通套接字无法处理ICMP\IGMP类型网络报文,使用socket.SOCK_RAW
# socket.SOCK_SOCKRDM 是一种可靠的UDP形式,即保证提交数据报也不保证顺序
#
# TCP与UDP对比:
# TCP(Transmission Control Protocol)是一种可靠的、面向连接的协议,传输效率低
# 全双工的通信,面向字节流
#
# UDP(User Datagram Protocol)是一种不可靠,无连接,传输效率高,可以实现一对一
# 一对多 多对多的通信,它的特点:尽最大努力服务,无阻塞控制。
#
# TCP工作流程:
# 服务器
# 1. 服务器初始化socket
# 2. 绑定端口
# 3. 端口监听
# 4. 调用accept阻塞,等待用户连接
#
# 客户端
# 1. 初始化socket
# 2. 连接服务器
# 3. 如果连接成功,客户端与服务器就建立了连接
#
# socket模块的函数用法:
import socket
# socket.socket(socket_family, socket_type,proto=0)
# socket_family 可以是AF_UNIX或者AF_INET
# socket_type 可以使SOCK_STREAM 或者SOCK_DGRAM
# protocol 一般不做设置,默认值0
#获取TCP格式套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 服务端:
tcpSock.bind() #绑定 主机,端口号到套接字
tcpSock.listen() #执行TCP监听
#客户端:
tcpSock.connect() #主动初始化TCP服务器连接
tcpSock.connect_ex() # connect()函数的拓展版本,在出错时返回出错码,而不是跑出异常
#公共用途的套接字:
tcpSock.recv() # 接收TCP数据
tcpSock.send() # 发送TCP数据
tcpSock.sendall()# 发送完整的TCP数据
udpSock.recvfrom() #接收UDP数据
udpSock.sendto() #发送UDP数据
2021-05-23
最新推荐文章于 2021-11-17 11:36:58 发布