1、简述socket通信原理?
什么是socket: Socket是一个网络通信的套接字(接口)
socket是应用层和传输层之间的一个抽象层,它把TCP/IP层负责的操作,抽象为几个简单的接口,供应用层调用实现进程在网络中的通信
套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
1. 网络通讯三要素:ip地址,端口号。传输协议
通信分为客户端和服务端
2、粘包的原因和解决办法
原因:只有TCP协议才有粘包现象,TCP是面向流式的协议,主要是接收方不知道消息之间的界限,不知道一次性接受多少字节造成的
例如:发送的的数据量比较小,时间间隔比较短,就合并成了一个包,
解决办法:引入struct模块,为字节流自定义报头 ,发送端:先发送报头的长度,再发送报头;接受端,先接受报头的长度,再接受报头
3、TCP协议的三次挥手,四次握手
三次握手:
client发送请求建立通道;
server收到请求并同意,同时也发送请求建通道;
client收到请求并同意,建立完成
四次挥手:
client发送请求断开通道;
server收到请求并同意,同时还回复client上一条消息;
server也发送请求断开通道;
client受到消息结束
TCP:可靠,对方确认收到消息,才发下一个,如果没有收到消息,就重发
UDP:不可靠,一直发数据,不需要对方回应
4、元类? 使用元类定义一个对象
元类是类的类,是类的模板
元类是用来控制如何创建类的,元类的主要目的是为了控制类的创建行为
4.1、寻找属性和方法的顺序问题:先从对象自己的命名空间中找,然后在自己的类中,最后在从父类当中去找
5、多态
一类事物有多种形态,多态性是指在不考虑实例类型的情况下使用实例;比如动物有人、猪、狗
不管是 人、狗、猪。都可以直接使用对象,
def func(obj):
obj.talk()
func(peo)
func(dog)
6、说一下__new__和__init__的区别
__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。
__new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法。
也就是,__new__在__init__之前被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。
7、反射的原理:
反射指的是程序可以访问、检测和修改它本身状态或行为的一种能力,也叫做自省。
反射就是通过字符串映射到对象的属性,python的一切事物都是对象(都可以使用反射)
1,hasattr(object,name) 判断object中有没有对应的方法和属性
判断object中有没有一个name字符串对应的方法或属性
2,getattr(object, name, default=None) 获取object中有没有对应的方法和属性
3,setattr(x, y, v) 设置对象及其属性
4,delattr(x, y) 删除类或对象的属性
8、python中实现静态方法和类方法都是依赖python的修饰器来实现的。静态方法是staticmethod,类方法是classmethod。
9、查看继承:__bases__类的父类构成的元组
10、说一下深度优先和广度优先的区别(新式类广度优先,经典类:深度优先)
11、import time
import hashlib
def create_id():
m = hashlib.md5(str(time.tine()).encode("utf-8"))
return m.hexdigest()
12、定义抽象类:
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def eat(self):
pass
1、简述可迭代对象、迭代器、生成器的关系. 注意列表生成式
(1)迭代器 Iterator是指可以被next()函数调用并不断返回下一个值的对象。,表示惰性计算序列。
(2)生成器:可以在循环过程中不断推算后续元素,这种一边循环一边计算的机制,称为生成器。(yeild)
(3)可作用于for循环的对象都是可迭代对象。
区别:迭代器由Class对象创建. 生成器由包含yield表达的Function对象或者Generator Expression创建.
生成器是迭代器的一种,可迭代对象不一定是迭代器。
迭代器有两个基本的方法:iter() 和 next()。
list/dic/str可以使用iter()函数变为迭代器。
优点:代器适合遍历一些数量巨大甚至无限的序列。
2、闭包的概念和作用
闭包:内部函数包含对外部作用域而非全局作用域的引用.(内部函数在外部函数返回后执行)
1、必须有内嵌函数
2、内嵌函数必须引用外部函数的对象
返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得该函数
无论在何处调用,优先使用自己外层包裹的作用域。装饰器就是闭包的应用
2.1、装饰器的概念和作用---必备条件:
装饰器(Decorator):python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,
装饰器的返回值也是一个函数对象(函数的指针)
2.1.1必备条件:
装饰器本质为函数(用来装饰其他函数)添加附加功能
1不能修改被装饰函数的源代码;
2不能修改函数的调用方式
高阶函数+嵌套函数 = 装饰器
3、Python中两种序列化json和pickle区别
序列化是把内存的数据类型转变为字符串(bytes类型)。
json:用于字符串和Python数据类型之间进行转换
pickle:用于Python特有类型(仅Python认识)和Python数据类型间进行转换。
shelve:将内存数据通过文件持久化,支持任何pickle支持的数据格式。
4、简述什么是类和对象
对象是特征与技能的结合体,类则是一系列对象相似的特征与技能的结合体
5、简述面向对象的三大特性
继承:什么是什么的关系,一种创建类的方式,解决代码重用问题。
多态:一类事物有多种形态,多态性是指在不考虑实例类型的情况下使用实例;比如动物有人、猪、狗
封装:明确区分内外,控制外部对隐藏属性的操作行为,优点:隔离复杂度
组合指的是,什么有什么的关系,在一个类中以另外一个类的对象作为数据属性,称为类的组合
6、简述什么是绑定方法和非绑定方法
(1)绑定到对象的方法:在类中定义没有加装饰器修饰的方法。
对象.bound_method() 自动将对象当做第一个参数传入
(2)绑定到类的方法:在类中定义的装饰器@classmethod修饰的方法。
类.bound_method() 自动将类当第一个参数传入
(3)非绑定方法:在类中用@staticmethod装饰器装饰的方法。静态方法是staticmethod
没有自动传值,不绑定类和对象,类和对象均可调用。
7、简述什么是反射以及实现反射的方法
反射指的是程序可以访问、检测和修改它本身状态或行为的一种能力,也叫做自省。
hasattr(obj, name): 判断obj对象是否有叫name字符串对应的方法或属性。
getattr(obj, name): 获取对象中name字符串对应的方法或属性。
setattr(x,'y',v):给对象赋值x.y=v; delattr(x,'y'):删除对象属性del x.y
7.1 什么是领域模型?
领域模型是对领域内的概念或现实世界中对象的可视化表示,
两个主要的作用:
发掘重要的业务领域概念,
建立业务领域概念之间的关系。
7.3 __call__对象后面加括号,出发执行
obj = Foo() # 执行 __init__ 实例初始化自动执行__init__
obj() # 执行 __call__
7.4、__init__()和__new__()方法:
发现实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法,__new__()必须要有返回值,返回实例化出来的实例
7.5、元类:元类是类的类,是类的模板,元类的主要目的是为了控制类的创建行为
8、简述TCP/IP各层功能(tcp/ip五层协议)
应用层:规定应用程序的数据格式。
传输层:建立端口到端口的通信。tcp/udp
网络层:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址,ip地址
数据链路层:定义了电信号的分组方式,分组方式后来形成了统一的标准,即以太网协议ethernet
物理层:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0
9、简述什么是Socket
网络通信的套接字
Socket是应用层与传输层TCP/IP协议族通信的中间软件抽象层,它是一组接口。把TCP/IP协议藏在接口内,直接去调用socket,即能符合协议要求。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
10、编写一个学生类, 要求有一个计数器的属性, 统计总共实例化了多少个学生。
class Student:
__count = 0
def __init__(self, name, age):
self.name = name
self.age = age
Student.__count += 1
@staticmethod
def get_count():
print("总共实例化 %s 人" % Student.__count)
stu1 = Student('hqs', 20)
stu2 = Student('egon', 19)
stu1.get_count()
Student.get_count()
10、基于socket和subprocess模块模拟实现循环执行命令。
########################server####################
import socket, subprocess, struct
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 9001)) # bind()内为元组,0-65535:0-1024供操作系统使用
server.listen(5)
print('starting...')
while True:
conn, client_addr = server.accept()
print(client_addr)
while True: # 通讯循环
try:
"""1、收到客户端的命令"""
cmd = conn.recv(8096)
if not cmd:break
"""2、执行命令,拿到结果"""
obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
"""3、命令结果返回客户端"""
# 第一步:制作固定长度的报头
total_size = len(stdout) + len(stderr) # 整型
header = struct.pack('i', total_size) # 隐患:struct的i 的取值区间是-2147483648~2147483648
# 第二步:将报头发给客户端
conn.send(header)
# 第三步:发送真实数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()
#############################client#####################
import time, socket, struct
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 9001))
while True:
"""1、发命令"""
cmd = input('>>: ').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
"""2、拿结果"""
# 第一步:拿到数据的长度——>即先收报头
header = client.recv(4)
# 第二步:从报头中解析出对真实数据的描述信息(数据长度)
total_size = struct.unpack('i', header)[0] # 解包之后为元组,取第一项即打包的内容
# 第三步:接收真实的数据
recv_size = 0
recv_data = b''
while recv_size < total_size:
res = client.recv(1024) # 最大不能超过操作系统缓存大小,一次收不完,多次收
recv_data += res
recv_size += len(res) # 最后一次收的时候将不是1024,未来如果要查看进度的时候有问题
print(recv_data.decode('utf-8'))
client.close()