IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【秋招】C++面试题 -> 正文阅读

[C++知识库]【秋招】C++面试题

sizeof

1.定义一个空的类型,里面没有任何成员变量和成员函数,对该类型求sizeof,得到的结果是多少?

答案:1

问:为什么不是0?

答案:空类型的实例中不包含任何信息,本来对其求sizeof应该是0,但是当我们在声明该类型实例的时候,它必须在内存中占一定的空间,否则无法使用这些实例。至于到底要占多少内存空间则由编译器决定。在visual studio中,每个空类型的实例占用1字节的空间。

2.sizeof 是什么?

答案:sizeof 是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小。

sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。

使用 sizeof 的语法如下:

sizeof (data type);
sizeof (object);
sizeof object;

3.各数据类型的sizeof大小?

32位64位
char11
int4大多数4少部分8
long48
float44
double88
指针48
short2
  1. sizeof(a)/sizeof(a[0]) 表示的是什么?

    答案: 可以获取数组的长度,原理是 sizeof(a) 代表整个数组的大小,sizeof(a[0]) 代表数组中第一个元素的大小,而数组中的每个元素大小都是相同的,所以最后的计算结果就是数组的长度。

    但是!这个问题也不绝对,如果数组作为参数传入到函数中,结果得到的就不是数组的大小了,sizeof(a)得到的是指针的大小。

  2. 字节对齐,sizeof(MyStruct)?
    struct MyStruct 
    { 
    double dda1; 
    char dda; 
    int type 
    }; 
    
    答案:16
    原因:各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照各自的对齐方式调整位置,空缺的字节Visual Studio会自动填充。
    (1)先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;
    
    (2)接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用 sizeof(char)=1个字节;
    
    (3)接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof (int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的(4)地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;
    (5)这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。 
    
    
  3. 常用类型(32位)的对齐方式:

    char偏移量必须为sizeof(char)1的倍数
    int偏移量必须为4的倍数
    float偏移量必须为4的倍数
    double偏移量必须为8的倍数
    short偏移量必须为8的倍数
  4. 问题:求以下结构体所占用的内存空间?sizeof(MyStruct)
    struct MyStruct 
    { 
    char dda; 
    double dda1; 
    int type 
    }; 
    答案:24bytes
    解析:偏移量为0,满足对齐方式,dda占用1个字节;
    下一个可用的地址的偏移量为1,不是sizeof(double)=8的倍数,需要补足7个字节才能使偏移量变为8(满足对齐方式),因此VC自动填充7个字节,dda1存放在偏移量为8的地址上,它占用8个字节。
    下一个可用的地址的偏移量为16,是sizeof(int)=4的倍数,满足int的对齐方式,所以不需要VC自动填充,type存放在偏移量为16的地址上,它占用4个字节。
    下一个可用的地址的偏移量为16,是sizeof(int)=4的倍数,满足int的对齐方式,所以不需要VC自动填充,type存放在偏移量为16的地址上,它占用4个字节。
    
    
    struct S3  
    {  
    };  
    sizeof(S3); //值为1,空结构体也占内存
    

9.数组的sizeof:

char a[10];  
char n[] = "abc";   
  
cout<<"char a[10]"<<sizeof(a)<<endl;//数组,值为10  
  
cout<<"char n[] = /"abc/"  "<<sizeof(n)<<endl;//字符串数组,将'/0'计算进去,值为4

解析:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。

     2)当数组为形参时,其sizeof值相当于指针的sizeof值。 

10.类中含有virtual时的sizeof?

类中含有virtual时(无论一个还是多个),内存中就会出现一个虚指针,而指针的大小为4,所以类的大小也是4;

单一继承下,无论父类还是派生类中存在虚函数,派生类中大小都为4(只考虑虚函数,无其他成员),虚函数地址都存在于虚函数指针指向的虚函数表中。

多重继承时,派生类会出现多个虚指针,此时大小就要具体计算了,如图继承两个含有虚函数的基类,派生类中存在两个虚指针,其大小为8。
class BaseAA{
public:
      virtual void AATest(){};
};
class BaseAAA:public BaseA,public BaseAA
{
vitrual void AATest(){};
};

size of (BaseAAA):8 bytes
某32位系统下, C++程序,请计算sizeof 的值
char str[] = “http://www.ibegroup.com/”
char *p = str ;
int n = 10;
请计算
sizeof (str ) = ?  //25
sizeof ( p ) = ?   //4
sizeof ( n ) = ?   //4

void Foo ( char str[100]){
请计算
sizeof( str ) = ?  //4
}

void *p = malloc( 100 );
请计算
sizeof ( p ) = ?   //4

  1. 题目一:在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但是不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7,的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数组2或者3。(n个元素,n种可能的取值)

    ##解法二开辟一个新数组B,每次扫描源数组A中的元素,如果不在B中,就加入B中,如果在B中,就找到一个重复的元素,时间复杂度log(n)
    ##python
    lisA=[8,5,3,3,7,6,3,9,1,8]
    def findDuplicates(lisA):
        lisB = []
        for i in lisA:
            if i in lisB:
                print("找到一个重复的元素:%d"%i)
                break
            else:                   #当前扫描的元素不在lisB中,就加入到lisB中
                lisB.append(i)
                continue
    findDuplicates(lisA)
    
    ##解法三因为列表总共有n个元素,所有元素可能取到的元素有0~n-1,共n种。如果不存在重复的数字,那么排序后数字i将会出现在下标为i的位置。现在让我们重新扫描数组,
    
    当扫描到下标为i的数字时,首先比较这个数字(记为m)与i是否相等:
    如果是,继续扫描下一个元素,
    如果不是,则再拿它与第m个数字比较:
    如果它和第m个数字相同,就找到了一个重复的元素;
    如果不同,就将m与第m个数字互换。接下来继续重头开始,重复换这个比较。
    ##python
    lisA=[8,0,2,3,7,6,4,2,1,5]
    def findDuplicates(lisA):
     
        i=0
        while i <len(lisA) and lisA!=[]:
            m = lisA[i]
            if m == i:    
                i += 1
            else:
                if m == lisA[m]:
                    print('找到一个重复的元素:%d' % m)
                    break
                else:
                    temp = lisA[i]
                    lisA[i] = lisA[m]
                    lisA[m] = temp
                    i = 0
     
    findDuplicates(lisA)
    
    

13.leetcode两数之和:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

你可以按任意顺序返回答案。

##最优解
def twoSum(self,nums, target):
    dct = {}
    for i, n in enumerate(nums):
        if target - n in dct:
            return [dct[target - n], i]
        dct[n] = i

##我的答案:
class Solution:
    def twoSum (self,nums,target):
        
        for i in range(len(nums)):
            for k in range(i+1,len(nums)):
                if nums[i]+nums[k]==target:
        
                    return i,k


if __name__=='__main__':
    s=Solution()
    s.twoSum([2,7,11,15],9)

14.时间复杂度与空间复杂度

时间复杂度简单来说指的是语句的执行次数,而空间复杂度表示算法所占的存储空间
按数量级递增排列,常见的时间复杂度有:
常数阶O(1),
对数阶O(log2n),
线性阶O(n),
线性对数阶O(nlog2n),
平方阶O(n^2),
立方阶O(n^3),…,
k次方阶O(n^k),
指数阶O(2^n)。

15.各算法复杂度自查表

img

Snipaste_2019-09-20_17-16-14

数组排序image-20190920164408891

图操作

image-20190920164509533

堆操作

image-20190920164555772

常见递归算法时间复杂度

image-20190920175238411

16.C++各版本的区别

C++真正正式公布的标准就三个:C++98、C++03、C++11。其中C++98是第一个正式C++标准,C++03是在C++98上面进行了小幅度的修订,C++11则是一次全面的大进化。
C++ 11

auto关键字
decltype关键字
nullptr字面值
constexpr关键字
for(declaration : expression)
Lambda表达式
initializer_list
标准库bind函数
智能指针shared_ptr,unique_ptr
右值引用&&
STL容器std::array,std::forward_list,std::unordered_map,std::unordered_set
 

C++ 14

拓展了lambda表达式,更加泛型:支持auto
拓展了类型推导至任意函数:C11只支持lambda返回类型的auto
弃用关键字 [[deprecated]]
 

C++ 17

拓展了constexpr至switch if等:C++11的constexpr函数只能包含一个表达式
typename 嵌套
inline 内联变量
模板参数推导
元组类 std::tuple:std::pair实现两个元素的组合,它实现多个
类模板std::variant表示一个类型安全的联合体。
引用包装器 std::reference_wrapper
变长参数模板
结构化绑定(函数多值返回时用{}合成struct)
非类型模板参数可传入类的静态成员
在if和switch中可进行初始化
初始化(如struct)对象时,可用花括号进行对其成员进行赋值
简化多层命名空间的写法
lambda表达式可捕获*this的值,但this及其成员为只读
十六进制的单精度浮点数
继承与改写构造函数
using B1::B1;//表示继承B1的构造函数
当模板参数为非类型时,可用auto自动推导类型
判断有没有包含某文件__has_include
[[fallthrough]]用于switch语句块内,表示会执行下一个case或default
[[nodiscard]]表示函数的返回值没有被接收,在编译时会出现警告。
[[maybe_unused]]即便没使用也不警告

17.C++与python的区别:

1.通过上面的分类,我们可以将**C++和python都归类为强类型语言。**python变量无需声明并不意味着就是弱类型,弱类型是指能够进行隐式转换,python是不能这么转换的,每个实例类型是固定的,转换实例类实际上是重新创建一个内存空间。

2.C++为编译型语言;python为解释型的脚本语言。

3.C++效率高,编程难;python效率低,编程简单。同样的功能,或许python可以很快的写出代码,但运行所需的时间需要成倍于C++。

1、运行效率:C++ >> Python
Python代码和C++最终都会变成CPU指令来跑,但一般情况下,比如反转和合并两个字符串,Python最终转换出来的CPU指令会比C++ 多很多。
首先,Python东西比C++多,经过了更多层,Python中甚至连数字都是object !!!
其次,Python是解释执行的,和物理机CPU之间多了解释器这层,而C++是编译执行的,直接就是机器码,编译的时候编译器又可以进行一些优化。
2、开发效率:Python >> C++
Python一两句代码就搞定的东西,C++往往要写一大堆。
一、文件结构不同

C++和Python在引用库的时候均需要将库import或者#include,但是在使用标准库的时候,Python不需要引入库。我觉得python应该会默认将库都加入进去,所以代码执行效率要比C++低一些,但是当Python引用第三方库的时候,两者是差不多的。

程序有两种执行方式,解释执行和编译执行。
PYTHON是一种脚本语言,是解释执行的,不需要经过编译,所以很方便快捷,且能够很好地跨平台,写一些小工具小程序特别合适。
而C++则是一种需要编译后运行语言,在特定的机器上编译后在特定的机上运行,运行效率高,安全稳定。但编译后的程序一般是不跨平台的。
2.垃圾回收机制:c++需要程序员收到回收,而java和python都有自己的垃圾回收机制GC。具体两者又有不同,python的垃圾收集机制主要是用的是引用计数方式。
c和java中变量的存储是真实值,而python总存储的是引用,所以python不用声明类型名称,它的输入均默认为字符串。
4.c++中用const来声明常量,java中使用final来声明,python中没有常量。

18.数据结构-树

树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:
1)有且仅有一个特定的称为根(Root)的结点;
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、......、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。

此外,树的定义还需要强调以下两点:
1)n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2)m>0时,子树的个数没有限制,但它们一定是互不相交的。
示例树:
图2.1为一棵普通的树:
由树的定义可以看出,树的定义使用了递归的方式。递归在树的学习过程中起着重要作用,如果对于递归不是十分了解,建议先看看递归算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rjp0g1UB-1653462331535)(C:\Users\sxj96\AppData\Roaming\Typora\typora-user-images\1614253601410.png)]

二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
图3.1展示了一棵普通二叉树:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sCmHyvQk-1653462331536)(C:\Users\sxj96\AppData\Roaming\Typora\typora-user-images\1614253983658.png)]

3.2 二叉树特点
由二叉树定义以及图示分析得出二叉树有以下特点:
1)每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
2)左子树和右子树是有顺序的,次序不能任意颠倒。
3)即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

3.3 二叉树性质
1)在二叉树的第i层上最多有2i-1 个节点 。(i>=1)
2)二叉树中如果深度为k,那么最多有2k-1个节点。(k>=1)
3)n0=n2+1 n0表示度数为0的节点数,n2表示度数为2的节点数。
4)在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]是向下取整。
5)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:

(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

19.网络编程和并发编程

网络编程和并发

简述 三次握手、四次挥手的流程。
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据。

四次分手:

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。

服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。

服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。

客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

网络Socket编程与Socket API的过程。

可以看到,只有当server端listen之后,client端调用connect才能成功,否则就会返回RST响应拒绝连接。

只有当accept后,client和server才能调用recv和send等io操作。

socket API调用错误不会导致client出现SYN_SENT状态,那么只能是网络设备丢包(路由器、防火墙)才会导致SYNC_SENT状态。

TCP断连过程说明。

close只是减少socket文件的引用计数,当计数减为0后,操作系统执行tcp的断连操作。

client端close后server端不close,会导致client端连接状态为FIN_WAIT_2,server端连接状态为CLOSE_WAIT。

正常编程肯定不会这样处理,一般都是在异常处理跳转(C++/JAVA等)导致没有close,或者整个系统异常导致没有close(例      如JVM内存出现out of memory错误)。

shutdown的处理逻辑比较复杂,非特殊情况不要乱用,很容易出问题。

进程退出后操作系统会自动回收socket,发起tcp关闭流程操作。

其中tcp三次握手与四次分手的各种状态的详解。

CLOSED: 这个没什么好说的了,表示初始状态即关闭状态。

  LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

  SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。

  SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。

  ESTABLISHED:这个容易理解了,表示连接已经建立了。

  FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别 是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

  FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

  TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

  CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什 么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

  CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

  LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

三次握手过程

四次分手过程

推荐博客:https://blog.csdn.net/libaineu2004/article/details/79020403,https://blog.csdn.net/qzcsu/article/details/72861891

什么是arp协议?
ARP,我们经常听到的这些术语,包括"网络扫描"、"内网渗透"、"中间人拦截"、"局域网流控"、"流量欺骗",基本都跟ARP脱不了干系。大量的安全工具,例如大名鼎鼎的Cain、功能完备的Ettercap、操作傻瓜式的P2P终结者,底层都要基于ARP实现。

ARP(Address Resolution Protocol)即地址解析协议, 用于实现从 IP 地址到 MAC 地址的映射,即询问目标IP对应的MAC地址。

在网络通信中,主机和主机通信的数据包需要依据OSI模型从上到下进行数据封装,当数据封装完整后,再向外发出。所以在局域网的通信中,不仅需要源目IP地址的封装,也需要源目MAC的封装。

一般情况下,上层应用程序更多关心IP地址而不关心MAC地址,所以需要通过ARP协议来获知目的主机的MAC地址,完成数据封装。

ARP协议通过"一问一答"实现交互,但是"问"和"答"都有讲究,"问"是通过广播形式实现,"答"是通过单播形式。

TCP和UDP的区别?
TCP面向连接(如打电话要先拨号建立连接)。UDP是无连接的,即发送数据之前不需要建立连接。

TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达。UDP尽最大努力交付,即不保证可靠交付。

TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流。UDP是面向报文的。

UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)。

每一条TCP连接只能是点到点的。UDP支持一对一,一对多,多对一和多对多的交互通信。

TCP首部开销20字节;UDP的首部开销小,只有8个字节。

TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

网络中的进程如何通信。

消息传递(管道、FIFO、消息队列)。

同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)。

共享内存(匿名的和具名的)。

远程过程调用(Solaris门和Sun RPC)。

什么是局域网和广域网?
局域网LAN(Local Area Network):一般指覆盖范围在10公里以内,一座楼房或一个单位内部的网络。由于传输距离直接影响传输速度,因此,局域网内的通信,由于传输于距离短,传输的速率一般都比较高。目前,局域网的传输速率一般可达到10MB/S和100MB/S,高速局域网传输速率可达到1000MB/S。

广域网WAN(Wide Area Network):是指远距离的、大范围的计算机网络。跨地区、跨城市、跨国家的网络都是广域网。由于广域的覆盖范围广,联网的计算机多,因此广域网上的信息量非常大,共享的信息资源很丰富。INTERNET是全球最大的广域网,它覆盖的范围遍布全世界。

城域网MAN(Metropolitan Area Network):其覆盖范围在局域网和广域网之间。一般指覆盖范围为一个城市的网络。

为何基于tcp协议的通信比基于udp协议的通信更可靠?
tcp: 可靠,对方给了确认收到信息,才发下一个,如果没收到确认信息就重发。

udp: 不可靠,一直发数据,不需要对方回应。

TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

基于tcp的数据传输,tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的。而udp发送数据,对端是不会返回确认信息的,因此不可靠。

什么是socket?简述基于tcp协议的套接字通信流程。
连接实际上是操作系统内核的一种数据结构,称为TCP控制块(TCB),对于linux而言是tcp_sock结构。不光连接,连数据包也是由一个数据结构来控制,linux里面称为sk_buff结构。

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

服务端:创建socket对象,绑定ip端口bind(),  设置最大链接数listen(),  accept()与客户端的connect()创建双向管道, send(), recv(),close()。

客户端:创建socket对象,connect()与服务端accept()创建双向管道 ,  send(), recv(),close()。

通过三次握手后建立连接,开始收发消息。收发消息完了之后,通过四次挥手断开连接。

什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?
粘包:多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发生方的发送边界,而采用某一估测值大小来进行数据读出,若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。

接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

当时短连接的情况下,不用考虑粘包的情况。

如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包。

缓冲区过大造成了粘包,所以在发送/接收消息时先将消息的长度作为消息的一部分发出去,这样接收方就可以根据接收到的消息长度来动态定义缓冲区的大小。(这种方法就是所谓的自定义协议,这种方法是最常用的)。

对发送的数据进行处理,每条消息的首尾加上特殊字符,然后再把要发送的所有消息放入一个字符串中,最后将这个字符串发送出去,接收方接收到这个字符串之后,再通过特殊标记操作字符串,把每条消息截出来。(这种方法只适合数据量较小的情况) 。

为什么基于TCP的通信程序需要封包、拆包?

TCP是流协议,所谓流,就是没有界限的一串数据。但是程序中却有多种不同的数据包,那就很可能会出现如上所说的粘包问题,所以就需要在发送端封包,在接收端拆包。

那么如何封包、拆包?

封包就是给一段数据加上包头或者包尾。比如说我们上面为解决粘包所使用的两种方法,其实就是封包与拆包的具体实现。

IO多路复用的作用?
服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种。

同步阻塞IO(Blocking IO):即传统的IO模型。

同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。

IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。

异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

SocketServer模块

ThreadingTCPServer

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求。

ForkingTCPServer

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 os.fork 两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,当前新创建的进程用来处理对应客户端的请求。

Twisted是一个事件驱动的网络框架,其中包含了诸多功能,例如:网络协议、线程、数据库管理、网络操作、电子邮件等。

Twisted本质上使用了事件驱动的方法和IO多路复用的机制来进行Socket的处理。

什么是防火墙以及作用?
防火墙作为一个边界防御工具,其监控流量——要么允许它、要么屏蔽它。 多年来,防火墙的功能不断增强,现在大多数防火墙不仅可以阻止已知的一些威胁、执行高级访问控制列表策略,还可以深入检查流量中的每个数据包,并测试包以确定它们是否安全。大多数防火墙都部署为用于处理流量的网络硬件,和允许终端用户配置和管理系统的软件。越来越多的软件版防火墙部署到高度虚拟化的环境中,以在被隔离的网络或 IaaS 公有云中执行策略。

select、poll、epoll 模型的区别?
只有IOCP是asynchronous I/O,其他机制或多或少都会有一点阻塞。

select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

epoll、 kqueue、select是Reacor模式,IOCP是Proactor模式。

java nio包是select模型。

Linux并发网络编程模型

Apache 模型,简称 PPC (Process Per Connection):为每个连接分配一个进程。主机分配给每个连接的时间和空间上代价较大,并且随着连接的增多,大量进程间切换开销也增长了,很难应对大量的客户并发连接。

TPC 模型( Thread Per Connection ):每个连接一个线程,和PCC类似。

select 模型,I/O多路复用技术。

每个连接对应一个描述。select模型受限于 FD_SETSIZE即进程最大打开的描述符数。linux2.6.35为1024,实际上linux每个进程所能打开描数字的个数仅受限于内存大小,然而在设计select的系统调用时,却是参考FD_SETSIZE的值。可通过重新编译内核更改此值,但不能根治此问题,对于百万级的用户连接请求,即便增加相应 进程数, 仍显得杯水车薪。

select每次都会扫描一个文件描述符的集合,这个集合的大小是作为select第一个参数传入的值。但是每个进程所能打开文件描述符若是增加了 ,扫描的效率也将减小。

内核到用户空间,采用内存复制传递文件描述上发生的信息。 

poll 模型:I/O多路复用技术。

poll模型将不会受限于FD_SETSIZE,因为内核所扫描的文件描述符集合的大小是由用户指定的,即poll的第二个参数。但仍有扫描效率和内存拷贝问题。

pselect模型:I/O多路复用技术。同select。

epoll模型:

无文件描述字大小限制仅与内存大小相关。

epoll返回时已经明确的知道哪个socket fd发生了什么事件,不用像select那样再一个个比对。

内核到用户空间采用共享内存方式,传递消息。

FAQ 

单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。 所以你有还是必要建立线程池来发挥更大的效能。 

如果fd被注册到两个epoll中时,如果有时间发生则两个epoll都会触发事件。

如果注册到epoll中的fd被关闭,则其会自动被清除出epoll监听列表。

如果多个事件同时触发epoll,则多个事件会被联合在一起返回。

epoll_wait会一直监听epollhup事件发生,所以其不需要添加到events中。

为了避免大数据量io时,et模式下只处理一个fd,其他fd被饿死的情况发生。linux建议可以在fd联系到的结构中增加ready位,然后epoll_wait触发事件之后仅将其置位为ready模式,然后在下边轮询ready fd列表。

简述 进程、线程、协程的区别 以及应用场景?
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

多线程:常见的浏览器、Web服务(现在写的web是中间件帮你完成了线程的控制),web处理请求,各种专用服务器(如游戏服务器)。

需要频繁创建销毁的优先用线程。

需要进行大量计算的优先使用线程(CPU频繁切换)。

多进程:目标子动能交互少,如果资源和性能许可,可以设计由多个子应用程序来组合完成目的。

1.进程是资源分配的最小单位,线程是CPU调度的最小单位。

2.进程是操作系统资源分配的单位。

3.线程是CPU调度的单位。

4.进程切换需要的资源最大,效率很低。

5.线程切换需要的资源一般,效率一般(当然在不考虑GIL的情况下)。

6.协程切换任务资源很小,效率高。

2.线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别

1.同一个进程中的线程共享同一内存空间,但是进程之间是独立的。

2.同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。

3.对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。

4.一个进程至少有一个进程

5.同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理(队列)来实现

6.创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。

7.一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。

8.线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。

3、协程与线程的比较

1. 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

2. 线程进程都是同步机制,而协程则是异步。

3. 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。

GIL是实现python解释器(CPython)时引入的一个概念,不是python特性。GIL是全局解释器锁,可以控制同一时刻只有一个线程能够运行,这样在跑多线程的情况下,只有当线程获取到全局解释器锁后才能运行,而全局解释器锁只有一个,因此即使在多核的情况下也只能发挥出单核的功能。另外在做IO操作时,GIL总是被释放。因此,IO密集型的python比计算密集型的程序更能利用多线程环境带来的便利。这对我们的程序究竟是选择多线程还是多进程大有帮助。

GIL锁是什么鬼?
我们所说的Python全局解释锁(GIL)简单来说就是一个互斥体(或者说锁),这样的机制只允许一个线程来控制Python解释器。

在IO密集型型操作下,多线程还是可以的。比如在网络通信,time.sleep()延时的时候。

在CPU密集型操作下,多线程性能反而不如单线程,此时只能用多进程。

Python中如何使用线程池和进程池?
进程池 multiprocessing.Pool

进程池 concurrent.futures.ProcessPoolExecutor

线程池 concurrent.futures.ThreadPoolExecutor

使用进程池来执行CPU密集型的任务,这样可以利用到多核的好处;使用线程池来处理IO型任务,根据实际情况来调整池的大小(线程过多时,切换线程的开销将会严重影响性能)。

threading.local的作用?
为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。

使用threading.local可以极大简化代码逻辑,同时保证各个子线程的数据安全。Threading.local最大的用处就是HTTP请求时绑定用户的信息,这样每个用户线程可以非常方便访问各自的资源而互不干扰。

进程之间如何进行通信?
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

管道:速度慢,容量有限,只有父子进程能通讯。

FIFO:任何进程间都能通讯,但速度慢。

消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题  

信号量:不能传递复杂消息,只能用来同步。

共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。

什么是并发和并行?
解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

解释三:在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群。

并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

进程锁和线程锁的作用?
线程锁:大家都不陌生,主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码。当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但是,其余线程是可以访问该对象中的非加锁代码块的。

进程锁:也是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,但是可以使用本地系统的信号量控制(操作系统基本知识)。

分布式锁:当多个进程不在同一个系统之中时,使用分布式锁控制多个进程对资源的访问。

解释什么是异步非阻塞?
同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。

同步:执行一个操作之后,等待结果,然后才继续执行后续的操作。

异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。

阻塞、非阻塞、多路IO复用,都是同步IO,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。真正的异步IO需要CPU的深度参与。换句话说,只有用户线程在操作IO的时候根本不去考虑IO的执行全部都交给CPU去完成,而自己只等待一个完成信号的时候,才是真正的异步IO。所以,拉一个子线程去轮询、去死循环,或者使用select、poll、epool,都不是异步。

路由器和交换机的区别?
交换机工作在中继层,交换机根据MAC地址寻址,路由器工作在网络层,根据IP地址寻址,路由器可以处理TCP/IP协议,而交换机不可以等等。

什么是域名解析?
域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站一种服务。域名解析也叫域名指向、服务器设置、域名配置以及反向IP登记等等。说得简单点就是将好记的域名解析成IP,服务由DNS服务器完成,是把域名解析到一个IP地址,然后在此IP地址的主机上将一个子目录与域名绑定。

生产者消费者模型应用场景及优势?
处理场景:处理数据比较消耗时间,线程独占,生产数据不需要即时的反馈等。

应用于一个生产数据一个处理数据的场景 。

优势生产者和消费者之间不直接进行通信 而是通过一个队列相当于一个缓冲区,平衡了生产者和消费者的处理能力。

什么是cdn?
CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。

大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源站的负载。

浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。

CDN的分流作用不仅减少了用户的访问延时,也减少的源站的负载。但其缺点也很明显:当网站更新时,如果CDN节点上数据没有及时更新,即便用户再浏览器使用Ctrl +F5的方式使浏览器端的缓存失效,也会因为CDN边缘节点没有同步最新数据而导致用户访问异常。

LVS是什么及作用?
LVS的英文全称是Linux Virtual Server,即Linux虚拟服务器。

LVS主要分为以下三部分:

Load Balancer 这是LVS的核心部分,它好比我们网站MVC模型的Controller。它负责将客户的请求按照一定的算法分发到下一层不同的服务器进行处理,自己本身不做具体业务的处理。另外该层还可用监控下一层的状态,如果下一层的某台服务器不能正常工作了,它会自动把其剔除,恢复后又可用加上。该层由一台或者几台Director Server组成。

Server Array 该层负责具体业务。可有WEB Server、mail Server、FTP Server、DNS Server等组成。注意,其实上层的Director Server也可以当Real server用的。

Shared Storage 主要是提高上一层数据和为上一层保持数据一致。 

前面我们说了LVS是工作在网络层。相对于其它负载均衡的解决办法,比如DNS域名轮流解析、应用层负载的调度、客户端的调度等,它的效率是非常高的。LVS的通过控制IP来实现负载均衡。IPVS是其具体的实现模块。IPVS的主要作用:安装在Director Server上面,在Director Server虚拟一个对外访问的IP(VIP)。用户访问VIP,到达Director Server,Director Server根据一定的规则选择一个Real Server,处理完成后然后返回给客户端数据。这些步骤产生了一些具体的问题,比如如何选择具体的Real Server,Real Server如果返回给客户端数据等等。IPVS为此有三种机制:

VS/NAT(Virtual Server via Network Address Translation),即网络地址翻转技术实现虚拟服务器。当请求来到时,Diretor server上处理的程序将数据报文中的目标地址(即虚拟IP地址)改成具体的某台Real Server,端口也改成Real Server的端口,然后把报文发给Real Server。Real Server处理完数据后,需要返回给Diretor Server,然后Diretor server将数据包中的源地址和源端口改成VIP的地址和端口,最后把数据发送出去。由此可以看出,用户的请求和返回都要经过Diretor Server,如果数据过多,Diretor Server肯定会不堪重负。

VS/TUN(Virtual Server via IP Tunneling),即IP隧道技术实现虚拟服务器。它跟VS/NAT基本一样,但是Real server是直接返回数据给客户端,不需要经过Diretor server,这大大降低了Diretor server的压力。

VS/DR(Virtual Server via Direct Routing),即用直接路由技术实现虚拟服务器。跟前面两种方式,它的报文转发方法有所不同,VS/DR通过改写请求报文的MAC地址,将请求发送到Real Server,而Real Server将响应直接返回给客户,免去了VS/TUN中的IP隧道开销。这种方式是三种负载调度机制中性能最高最好的,但是必须要求Director Server与Real Server都有一块网卡连在同一物理网段上。 

Nginx是什么及作用?
Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。

Nginx的作用是 反向代理,负载均衡。其特点是占有内存少,并发能力强。

keepalived是什么及作用?
keepalived是一个类似于Layer2,4,7交换机制的软件。是Linux集群管理中保证集群高可用的一个服务软件,其功能是用来防止单点故障。

keepalived是基于VRRP协议实现的保证集群高可用的一个服务软件,主要功能是实现真机的故障隔离和负载均衡器间的失败切换,防止单点故障

haproxy是什么以及作用?
 高可用性,负载平衡和用于TCP和基于http的应用程序的代理。

什么是负载均衡?
负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外供应效力而无须其他服务器的辅助。经过某种负载分管技术,将外部发送来的央求均匀分配到对称结构中的某一台服务器上,而接收到央求的服务器独登时回应客户的央求。均衡负载可以平均分配客户央求到服务器列阵,籍此供应快速获取重要数据,解决很多并发访问效力问题。这种群集技术可以用最少的出资取得接近于大型主机的性能。

什么是rpc及应用场景?
同步调用 客户方等待调用执行完成并返回结果。 异步调用 客户方调用后不用等待执行结果返回,但依然可以通过回调通知等方式获取返回结果。 若客户方不关心调用返回结果,则变成单向异步调用,单向调用不用返回结果。

RPC 的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。

简述 asynio模块的作用和应用场景。
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

简述 gevent模块的作用和应用场景。
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成。

twisted框架的使用和应用?
Twisted是用Python实现的基于事件驱动的网络引擎框架,Twisted支持许多常见的传输及应用层协议,包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。就像Python一样,Twisted也具有“内置电池”(batteries-included)的特点。Twisted对于其支持的所有协议都带有客户端和服务器实现,同时附带有基于命令行的工具,使得配置和部署产品级的Twisted应用变得非常方便。

20.OSI七层协议

简述 OSI 七层协议。
应用层(Application)、表示层(presentation)、会话层(session)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。

每一层实现各自的功能和协议,并完成与相邻接口通信,OSI的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。各层所提供的服务与这些服务是怎么实现的无关。

应用层
OSI 参考模型中最靠近用户的一层,是为计算用户提供应用接口,也为用户直接提供网络服务。常见的应用层网络服务协议有:HTTP,HTTPS,FTP,POP3,SMTP等

表示层
表示提供各种用于应用层数据编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要该层可提供一种标准表示形式,用于将计算机内部的多种数据格式转换成通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。

会话层
会话层负载建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。

传输层
传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题。该层向高层屏蔽了下层数据通信的细节,是高层用户看到的只是在两个传输实体建的一个主机到主机的、可由用户控制和设定、可靠的数据通路。通常说的TCP UDP就是在这层。端口号即是这里的“端”。

网络层
本层通过IP寻址来建立两点之间的连接,为源端的运输层来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。就是通常说的ip层。这一层就是我们经常说的IP协议层。IP协议是Internet的基础。

数据链路层
将比特组合成字节,再将字节组成帧,使用链路层地址(以太网mac地址)来访问介质,并进行差错检测。

数据链路层又分为2个子层:逻辑链路控制子层(LLC)和媒体访问控制子层(MAC)。

物理层
实际最终信号传输是通过物理层实现的。通过物理介质传输比特流。规定电平、速度和电缆针脚。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。这些都是物理层的传输介质。

21.c/s架构和b/s架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wR7gvBwq-1653462331536)(C:\Users\sxj96\AppData\Roaming\Typora\typora-user-images\1614396944267.png)]

什么是C/S和B/S架构?
C/S架构是一种比较早的软件架构,主要应用于局域网内。在这之前经历了集中计算模式,随着计算机网络的进步与发展,尤其是可视化工具的应用,出现过两层C/S和三层C/S架构,不过一直很流行也比较经典的是我们所要研究的两层C/S架构。

C/S架构的优点

a. 客户端和服务器直接相连。点对点的连接方式更安全,可以直接操作本地文本,比较方便。

b. 客户端可以处理一些逻辑事务。可以进行数据处理和数据存储,提供一定的帮助。

c. 客户端直接操作界面。

3. C/S架构的缺点

a. C/S架构适用于局域网,对网速的要求比较高。

b.客户端界面缺乏通用性,且当业务更改时就需要更改界面,重新编写。

c. 随着用户数量的增多,会出现通信拥堵、服务器响应速度慢等情况。

d.系统的维护也比较麻烦。

C/S架构的应用

C/S架构的软件是在是数不胜数,从办公的OFFICE,WPS,WINRAR到杀毒软件如金山,瑞金再到我们的娱乐软件,如播放器,QQ,微信等,还有游戏等等,无处不见C/S架构。

 B/S架构及其背景

随着Internet和WWW的流行,以往的主机/终端和C/S都无法满足当前的全球网络开放、互连、信息随处可见和信息共享的新要求,于是就出现了B/S型模式,即浏览器/服务器结构。它是C/S架构的一种改进,可以说属于三层C/S架构。主要是利用了不断成熟的WWW浏览器技术,用通用浏览器就实现了原来需要复杂专用软件才能实现的强大功能,并节约了开发成本,是一种全新的软件系统构造技术。

B/S架构的优点

a. 浏览器和数据库服务器采用多对多的方式连接。因此适合在广域网里实现巨大的互联网,甚至是全球网,有着很强大的信息共享性。

b. 浏览器只处理一些简单的逻辑事务,负担小。

c.数据都集中存放在数据库服务器,所以不存在数据不一致现象。

d.随着服务器负载的增加,可以平滑地增加服务器的个数并建立集群服务器系统,然后在各个服务器之间做负载均衡。

e.B/S建立在广域网上,所以需要的网速要求不高。

f.不需要安装客户端,只要能连上网,就能随时随地的浏览页面。

g.能有效地保护数据平台和管理访问权限,确保服务器数据库的数据安全。

B/S架构的缺点

a. 服务器承担着重要的责任,数据负荷较重。一旦发生服务器“崩溃”等问题,后果不堪设想。

b.页面需要不断地动态刷新,当用户增多时,网速会变慢。

B/S架构的应用

比如WEBQQ,从WEBQQ名称中的WEB就不难看出它属于B/S架构,是一种浏览器服务器结构。事实上也是如此,因为WEBQQ根本不需要安装客户端,只需要有浏览器就可以进行聊天交互了。

22.链表、栈、队列的区别

链表:
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在由一个个节点组成,每个节点(node)中储存着数据变量(data)和指针变量(node next),又有一个头节点(head)连接下面的节点,而最后一个节点指向空(null)。可以在链表类中定义增加,删除,插入,遍历,修改等方法,故常用来储存数据。  
栈:
栈(Stack),是硬件。主要作用表现为一种数据结构,是只能在某一端插入和删除的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

    栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)。栈也称为先进后出表。   
队列:
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。        

在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将最后被删除的元素,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。   

区别;
2.1、队列
队列 是一种数据结构,其特点是先进先出,后进后出,只能在队首删除,在队尾增加。
队列的存储方式 既可以使用 线性表 进行存储,也可以使用 链表 进行存储。

2.2、栈
栈 是一种数据结构,只能在一端进行插入和删除操作的特殊线性表,按照先进后出 (FILO)的原则存储数据。

2.3、链表
链表 是一种数据的存储方式(与其对应的是 顺序存储 结构),存储的数据在内存中 不连续的 ,用 指针 对数据进行访问。

链表实际上可以认为是一种数据的物理组织形式,是用指针或对象的引用组织起的一种数据的存储方式.   
队列和堆栈是一个更高层次的概念,其底层可以是用链表也可以是用数组来实现.   
队列和堆栈的主要区别是进出的顺序不一样,   
队列是先进先出,堆栈是后进先出.   

数组与链表是更加偏向数据存储方式的概念,数组在连续的空间中存储数据,随机读取效率高,但是数据添加删除的效率较低; 而链表可以在非连续的空间中存储数据,随机访问效率低,数据添加删除效率高。
队列和栈是描述数据存取方式的概念,队列是先进先出,而堆栈是后进先出;队列和栈都可以使用数组或者链表实现。

23.线性表-----------顺序表

线性表有两种表示形式:
①顺序表示(数组)
②链表表示

顺序表优点:
随机访问特性,查找O(1)时间,存储密度高;
逻辑上相邻的元素,物理上也相邻;
无须为表中元素之间的逻辑关系而增加额外的存储空间;
顺序表示的缺点:
存储空间分配不灵活,
运算的空间复杂度高

顺序表的封装需要三个属性:
存储空间的起始位置。数组data的存储位置就是线性表存储空间的存储位置
线性表的最大存储容量。数组长度MAXSIZE
线性表的当前长度。length

注意:数组的长度与线性表的当前长度是不一样的。数组的长度是存放线性表的存储空间的总长度,一般初始化后不变。而线性表的当前长度是线性表中元素的个数,是会改变的。

按位查找的时间复杂度为O(1) 
按值顺序查找,最好的时间复杂度O(1),最差的时间复杂度O(n)
顺序表的插入:
线性表的操作包括如下几种
  (1) InitList(& L)
        //构造一个空的线性表 
  (2) DestroyList(& L)
       //初始条件:已经存在一个线性表L
       //线性表存在了,消耗一个线性表
  (3) ClearList(&L )
       //清空线性表中的内容
  (4) ListEmpty(L)
       //判断线性表是否为空
  (5) ListLength(L)
        //返回线性表的长度
  (6) GetElem(L,i,& e)
        //返回线性表i位置上的元素值,通过e返回     
  (7) PriorElem(L,cur_e,&pre_e)
       //如果cur_e是线性表中的元素,而且不是第一个,那么我们就可以返回该元素前一个元素的值
  (8) NextElem(L,cur_e,&next_e)
       //如果cur_e是线性表中的元素,而且不是最后一个,就返回它下一个元素的值
  (9) ListInsert(&L,i,e)
        //如果线性表存在了,而且i符合条件,则在i位置插入一个元素
       //最好时间复杂度O(1),最差时间复杂度O(n)
  (10)ListDelete(&L,i)
       //删除i位置上的元素
       //最好时间复杂度O(1)
       //最差时间复杂度O(n)
 (11) ListDelete_data(&L,e,order)
      //删除指定的元素e,order决定是删除一个,还是全部。
 (12) Connect_two_List(L_a,L_b,& L_c)
      //连接两个线性表,除去重复的内容    
 (13)print(L)
       //打印线性表    

24.线性表-------链表

在创建一个链表的时候,无需知道链表的长度。

链表初始化的作用就是生成一个链表的头指针,以便后续的函数调用操作。在没有任何输入的情况下,我们首先需要定义一个头指针用来保存即将创建的链表。所以函数实现过程中需要在函数内定义并且申请一个结点的空间,并且在函数的结尾将这个结点作为新建链表的头指针返回给主调函数,此处例程中返回的链表指针为该链表的头结点,:
linklist *  List_init()
{
    linklist *HeadNode= (linklist*)malloc(sizeof(linklist));

    if(HeadNode == NULL)

    {
        printf("空间缓存不足");

        return HeadNode;

    }

    HeadNode->Element= 0;

    HeadNode->next= NULL;

 

    returnHeadNode;
}

链表的特点:
(1)节点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
(2)访问时只能通过头指针进入链表,并通过每个节点的指针域依次向后顺序扫描其余节点,所以寻找第一个结点和最后一个结点所花费的时间不等。
链表 n个结点由指针域组成一个链表,链表是顺序存储的。
-单链表:每个结点只有一个指针域,单链表由头指针唯一确定
-双链表:每个结点有两个指针域
-循环链表:链表的首尾结点相接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MxJ5XQjm-1653462331537)(C:\Users\sxj96\AppData\Roaming\Typora\typora-user-images\1614858010353.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRW0i2yI-1653462331537)(C:\Users\sxj96\AppData\Roaming\Typora\typora-user-images\1614858060036.png)]

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-27 17:12:03  更:2022-05-27 17:13:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 21:21:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码