1.黏包
(一)发生黏包
a.# ###服务端
import socket
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
#三次握手
conn,addr=sk.accept()
#收发数据逻辑
conn.send("hello,".encode())
conn.send("world".encode())
#四次挥手
conn.close()
#退还端口
sk.close()
b.# ###客户端
import socket,time
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)
print(sk.recv(10))
print(sk.recv(10))
sk.close()
"""
总结:导致黏包现象的两种情况
hello,world
(1)在发送端,发送数据太快,频繁发送
(2)在接收端,接收数据太慢,延迟截取
"""
(二)解决方法一
a.### #服务端
import socket
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr=sk.accept()
#收发数据的逻辑
#告诉接收端,我要发送的数据长度是多少
conn.send("6".encode())
#发送的实际数据
conn.send("hello,".encode())
conn.send("world".encode())
conn.close()
sk.close()
b.### #客户端
import socket,time
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)
#收发数据逻辑
n=int(sk.recv(1).decode())
print(n,type(n))
print(sk.recv(n))
print(sk.recv(10))
sk.close()
(三)解决方法二
a.### #服务端
import socket
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr=sk.accept()
#收发数据的逻辑
#告诉接收端,我要发送的数据长度是多少
conn.send("00000120".encode())
#发送的实际数据
msg="hello," * 20
conn.send(msg.encode())
conn.send("world".encode())
conn.close()
sk.close()
b.### #客户端
import socket,time
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)
#收发数据逻辑
n=int(sk.recv(8).decode())
print(n,type(n))
print(sk.recv(n))
print(sk.recv(10))
sk.close()
(四)解决方法三
a.# ###服务端
import socket,struct
sk=socket.socket()
sk.bind(("127.0.0.1",9000))
sk.listen()
conn,addr=sk.accept()
#收发数据逻辑
inp=input("message>>>:")
msg=inp.encode()
#把长度的数字转换成二进制字节流,然后发送给对面,按照这么大的长度进行截取
res=struct.pack("i",len(msg))
conn.send(res)
conn.send("world".encode())
#四次挥手
conn.close()
#退还端口
sk.close()
"""
字节流是一个一个字节组成的,凑在一起,就是字节流
总长度就是一共的字节数,用len算字节流长度,告诉接收端,要截取多少个字节
"""
# ###客户端
import socket,time,struct
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)
#先接收要截取的长度是多少
n=sk.recv(4)
n=struct.unpack("i",n)[0]
print(n)
#再去接收真实的数据,防止黏包
print(sk.recv(n))
print(sk.recv(10))
sk.close()
b.# ###客户端
import socket,time,struct
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
time.sleep(0.1)
#先接收要截取的长度是多少
n=sk.recv(4)
n=struct.unpack("i",n)[0]
print(n)
#再去接收真实的数据,防止黏包
print(sk.recv(n))
print(sk.recv(10))
sk.close()
2.struct的基本用法
#struct 基本用法
import struct
#pack
"""
#struct.pack 把任意长度的数字转化成具有固定长度的4个字节的值,组成字节流
pack("i/f/s",2100000000) 代表我要转换的这个数据类型是整型,这个整型一般放的是字节长度;
i ==>int
"""
#unpack
"""
#struct.unpack 把4个字节的值恢复成原有的数据,返回的是元组
uppack("i") 代表我要转换的这个数据类型是整型,这个整型一般放的是字节长度;
"""
res=struct.pack("i",10000) #b"x10'x00x00"
res=struct.pack("i",2100000000) #b'x00xe1xf5x05'
#小于22亿的长度范围
# res=struct.pack("i",13000000000) #erro
print(res)
print(len(res)) #4
# "i" 把二进制字节流转换成整型,返回的是元组,通过下标0直接拿到数据
res=struct.unpack("i",res)[0]
print(res) #2100000000
3.### #socketserver 实现tcp链接的并发操作
a.###服务端
"""
文件<==>模块
文件夹<==>包
"""
import socketserver
class Myserver(socketserver.BaseRequestHandler):
#默认调用handle
def handle(self):
#self.request 相当于socketserver 底层已经给你封装好了,直接拿来用就可以;
conn=self.request
print(self.client_address)
while True:
msg=conn.recv(1024).decode("utf-8")
print(msg)
conn.send(msg.upper().encode("utf-8"))
#创建一个对象 ThreadingTCPServer 创建 ThreadingTCPServer((ip,port),自定义类)
server=socketserver.ThreadingTCPServer(("127.0.0.1",9000),Myserver)
#循环调用
server.serve_forever()
b.### #客户端
import socket
sk=socket.socket()
sk.connect(("127.0.0.1",9000))
while True:
sk.send(b"hello")
msg=sk.recv(1024)
print(msg)
# sk.recv()
sk.close()
5.# ###hashilib 模块
import hashlib
import random
#基本用法
#(1)创建一个md5算法的对象
hs=hashlib.md5()
#(2) 把想要加密的字符串通过update进行更新到hs对象中进行处理
hs.update("abc@123".encode())
#(3)返回32位16进制的字符串
res=hs.hexdigest()
print(res,len(res)) #b24331b1a138cde62aa1f679164fc62f 32
#加盐 (key 只有自己知道的关键字,目的就是增加密码的复杂度)
hs=hashlib.md5("Xboy_".encode("utf-8"))
hs.update("abc@123".encode())
res=hs.hexdigest()
print(res) #6036d50f3aac0502f96391543ccf06a6
#动态加盐
res=str(random.randrange(10000,100000))
hs=hashlib.md5(res.encode())
hs.update("abc@123".encode())
res=hs.hexdigest()
print(res) #c4d025cb4c90cc33ed4d91bfd9428f62 不断变化
"""
#md5 加密效率快,安全性不是太高,位数32位的16进制的字符串
#sha 加密效率慢,安全性稍高,更加精度,位数是40位的16进制字符串
#sha512 加密效率慢,安全性稍高,更加精确,位数是128位的16进制字符串
"""
#sha 算法系列
# hs=hashlib.sha1()
hs=hashlib.sha512()
hs.update("abc@123".encode())
hm=hs.hexdigest()
print(hm) #ddac418a1be76098d01107464026f65d2a3192bf
# ###hmac
"""加密的字符串强度更高,不容易破解"""
import hmac
key=b"xboyww"
msg=b"abc@123"
hm=hmac.new(key,msg)
res=hm.hexdigest()
print(res,len(res)) #ead7141d73206994b399111ac7baa4dc
#随机返回长度为32位的二进制字节流
import os
key=os.urandom(32)
#b"Rxb5dPxbexe5_x1dx05x92xa6xe2Rxbcx13xddxd5Uxb1'xf4xf3x13nx1dx82T3xfcx80a*"
print(key,len(key))
hm=hmac.new(key,msg)
res=hm.hexdigest()
print(res) #32位变化的 随urandom变化而变化
6.# ###文件校验
import hashlib
"""
read 在mode="r" 读取的单位是字符;
read 在mode="rb" 读取的单位是字节;
"""
"""
with open("ceshi111",mode="r",encoding="utf-8") as fp:
res=fp.read(3)
print(res) #宇宙无
with open("ceshi111",mode="rb") as fp:
res=fp.read(3)
print(res) #b'xe5xaex87'
print(res.decode()) #宇
"""
#(1)针对小文件的内容校验
def check_md5(file):
with open("ceshi111",mode="rb") as fp:
hs=hashlib.md5()
hs.update(fp.read())
return hs.hexdigest()
#如果两个加密的32位字符串相同,就可以说明两个文件的内容是一样的
res1=check_md5("ceshi111")
res2=check_md5("ceshi222")
print(res1)
print(res2)
# (2) 针对大文件的内容校验
hs=hashlib.md5()
hs.update("昨天晚上 拉肚子了。。。".encode())
hm=hs.hexdigest()
print(hm) #a4ee21e6c85406587ec2bf552019bce1
#可以利用update,分次更新内容
#利用update 这个特性,可以把较大的内容分次进行加密
hs=hashlib.md5()
hs.update("昨天晚上 ".encode())
hs.update("拉肚子了。。。".encode())
hm=hs.hexdigest()
print(hm) #a4ee21e6c85406587ec2bf552019bce1
#方法一 不停的读字节,直到为空的时候,终止循环
def check_md5(file):
#创建对象
hs=hashlib.md5()
with open(file,mode="rb") as fp:
while True:
#按照每次读取一个字节
contenty=fp.read(1)
#如果读取的是空字节,直接break
if contenty:
hs.update(contenty)
else:
break
return hs.hexdigest()
print("-----------")
#6a37ee1a2aab185df995beca6351231d 加空格的md5
print(check_md5("ceshi111")) #331f05042466475cba193bfa49caa463
print(check_md5("ceshi222")) #331f05042466475cba193bfa49caa463
# 方法二 不停减去响应的字节数,直到减到0,循环终止
import os
#计算文件大小,os.path.getsize(file)
def check_md5(file):
file_size=os.path.getsize(file)
# print(file_size)
hs=hashlib.md5()
with open(file,mode="rb") as fp:
while file_size:
content=fp.read(1)
hs.update(content)
#按照实际读取的字节数进行相减
file_size-=len(content)
return hs.hexdigest()
print("-----")
print(check_md5("ceshi111"))
print(check_md5("ceshi222"))
7.# ###进程
#获取进程号 =>相当于身份证,唯一
import os
#获取当前进程ID
# res=os.getpid()
# print(res) #一直变化
# #获取父进程
# res=os.getppid()
# print(res) #407 不变
#(1)进程的基本用法
from multiprocessing import Process
import time
"""
def func():
print("222>>>当前子进程ID>>>:%s,它的父进程ID>>>:%s"%(os.getpid(),os.getppid()))
if __name__ == '__main__':
print("111>>>1.子进程:%s,2.父进程:%s"%(os.getpid(),os.getppid()))
#创建子进程
'''target=函数,单独用一个进程去执行谁,去完成哪个任务'''
p=Process(target=func)
#调用子进程
p.start()
"""
"""
222>>>1.子进程:1226,2.父进程:407
111>>>当前子进程ID>>>:1227,它的父进程ID>>>:1226
"""
#(2) 带有参数的函数
'''
异步程序:不等每一行代码执行结束,就往下执行其他代码是异步程序
创建进程的时候,需要创建 -> 就绪,cpu才能过来执行绪态的程序
创建进程时,需要分配空间,分配空间时,会出现阻塞现象
'''
'''
def func():
for i in range(5):
print("222>>>当前子进程ID>>>:%s,它的父进程ID>>>:%s" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
print("111>>>1.子进程:%s,2.父进程:%s" % (os.getpid(), os.getppid()))
#创建进程,返回进程对象
p=Process(target=func)
p.start()
n=5
for i in range(1,n+1):
print("*" *i )
'''
'''
def func(n):
for i in range(1,n+1):
time.sleep(0.1)
print("222>>>当前子进程ID>>>:%s,它的父进程ID>>>:%s" % (os.getpid(), os.getppid()))
if __name__ == '__main__':
print("111>>>1.子进程:%s,2.父进程:%s" % (os.getpid(), os.getppid()))
n=5
"""args=(参数1,参数2.....),args类型是元组"""
p=Process(target=func,args=(n,))
p.start()
for i in range(1, n + 1):
time.sleep(0.1)
print("*" * i)
'''
'''
111>>>1.子进程:1404,2.父进程:407
222>>>当前子进程ID>>>:1405,它的父进程ID>>>:1404
*
222>>>当前子进程ID>>>:1405,它的父进程ID>>>:1404
**
222>>>当前子进程ID>>>:1405,它的父进程ID>>>:1404
***
222>>>当前子进程ID>>>:1405,它的父进程ID>>>:1404
****
222>>>当前子进程ID>>>:1405,它的父进程ID>>>:1404
*****
'''
#(3) 进程之间的数据,彼此是隔离的
'''
count=99
def func():
global count
count=100
print(count)
print("当前子进程ID号:%s"%(os.getpid()))
if __name__ == '__main__':
#创建主进程
p=Process(target=func)
p.start()
#为了先让子进程跑完,在执行过程中的count,看看是否通过子进程进行了修改
time.sleep(1)
print("我是主进程",count)
'''
#(4) 多进程之间的并发
"""
在程序并发时,因为cpu的调度策略问题,不一定谁先执行,谁后执行
但是如果遇到阻塞一定会进行切换,任务的执行是互相抢占cpu资源的过程
以目前程序来看,主进程执行的稍快,子进程稍慢
主进程和子进程齐头并进往前跑,谁在前不确定,依赖cpu的调度
"""
'''
def func(args):
print("222>>>当前子进程ID>>>:%s,它的父进程ID>>>:%s"%(os.getpid(),os.getppid()))
print("end",args)
if __name__ == '__main__':
for i in range(10):
Process(target=func,args=(i,)).start()
print("主进程执行结束")
'''
#(5)主进程和父进程之间的关系
"""
主进程执行完所有代码,开始等待,等待所有子进程全部结束之后,再终止程序
若不等待,主进程终止,子进程变成僵尸程序
在后台不停的运行,占用内存和cpu
因为进程太多,不容易找到,不容易管理
所以主进程跑完后,再彻底结束程序
"""
def func(args):
print("222>>>当前子进程ID>>>:%s,它的父进程ID>>>:%s"%(os.getpid(),os.getppid()))
time.sleep(0.1)
print("end",args)
if __name__ == '__main__':
for i in range(10):
Process(target=func,args=(i,)).start()
print("主进程执行结束")
# ###jion 功能:等待子进程执行完毕之后,主进程在向下执行
from multiprocessing import Process
import time,os
"""
8.(1) jion 基本用法
def func():
print("发送第一份邮件")
if __name__ == '__main__':
p=Process(target=func)
p.start()
# time.sleep(1)
'''针对p进程对象来说,必须等p这个进程执行完毕,主进程的代码向下执行'''
p.join()
print("发送第二份邮件")
"""
#(2) 多个子进程通过join加阻塞,可以实现同步控制
"""
def func(index):
print("第%s封邮件已经发送。。。"%index)
# time.sleep(1)
if __name__ == '__main__':
lst=[]
for i in range(10):
#创建进程 1个主进程 + 10个子进程=11个进程 创建异步程序
p=Process(target=func,args=(i,))
#调用进程
p.start()
lst.append(p)
#如果把join加到循环里,当前这个进程对象,join必须执行结束,下一个进程对象才能创建,变成同步程序,而进程的提示是为了提升执行的速度
# p.join()
#列表中的每个进程对象,都加上一个join,可以让所有的进程对象都执行完毕,就释放阻塞,往下执行,保证子进程和主进程之间的同步性
for i in lst:
i.join()
# print(lst)
# p.join()
print("发送最后一封邮件")
"""
### #使用第二种方法创建进程
"""用自定义类的方式创建进程"""
#(1) 基本使用
#必须继承父类Process类
class MyProcess(Process):
#类似于handle,必须写成run方法
def run(self):
print("子进程:%s,父进程:%s" % (os.getpid(),os.getppid()))
if __name__ == '__main__':
p=MyProcess()
p.start()
print("主进程:{}".format(os.getpid()))
# (2) 带参数的子进程函数
class MyProcess(Process):
def __init__(self,args):
#必须调用父类的构造方法
super().__init__()
#把参数通过arg来进行保存
self.args=args
#类似于handle,必须写成run方法
def run(self):
print("子进程:%s,父进程:%s" % (os.getpid(),os.getppid()))
if __name__ == '__main__':
lst=[]
#进程的并发是异步程序
for i in range(10):
p=MyProcess("参数:%s"%i)
p.start()
lst.append(p)
#等待所有子进程结束再执行主进程代码是同步程序
for i in lst:
i.join()
print("最后打印子进程ID:",os.getpid())
9.###守护进程 daemon
from multiprocessing import Process
import time
"""
守护进程语法:
进程对象.daemon=True
设置该对象为守护进程
守护进程需要在start()方法之前设置
守护进程为主进程守护,主进程代码执行完毕了,该守护进程自动终止
但其他子进程全部执行完毕之后,主进程彻底终止
"""
#(1) 基本语法
"""
def func():
print("子进程start")
time.sleep(0.2)
print("子进程end")
if __name__ == '__main__':
p=Process(target=func)
#在start开始之前设置该进程时守护进程
p.daemon=True
p.start()
time.sleep(2)
print("主进程执行完毕")
"""
#(2) 多个子进程的情况
'''
2个子进程+1个主进程
当主进程里面的代码全部执行完毕之后,守护进程自动终止;
因为func2这个任务进程执行完毕,所有主进程不能立刻终止程序
代码执行完毕
和程序执行完毕两回事
代码执行完毕 意味着守护进程立刻终止
只有非守护进程(func2)也都执行完毕之后,主进程才会真正结束
func1 是守护进程
func2 是非守护进程就是一个普通进程
默认主进程会等待所有进程执行完毕之后,才会最终终止程序;
子进程和主进程彼此独立,数据也不共享,为了防止僵尸程序,才是等待的意义
'''
"""
def func1():
count=1
while True:
print("*" * count)
time.sleep(0.5)
count+=1
def func2():
print("func2 start")
time.sleep(3)
print("func2 end")
if __name__ == '__main__':
p1=Process(target=func1)
p1.daemon=True
p2=Process(target=func2)
p1.start()
p2.start()
time.sleep(1)
print("主进程代码执行完毕")
"""
#(3) 守护进程的实际用途:报活
def alive():
while True:
print("1号服务器主机。。。。i am ok.....")
time.sleep(0.5)
def func():
print("1号服务器主要负责统计mysql日志")
time.sleep(3)
if __name__ == '__main__':
p1=Process(target=alive)
p1.daemon=True
p2=Process(target=func)
p1.start()
p2.start()
#用join 添加一下阻塞,如果join执行结束了,就代表服务器统计日志的功能失效了
#或者服务器奔溃,机器也会终止程序
p2.join()
print("--------")