面试之——网络编程相关

1、 三次握手和四次挥手

SYS(  synchronize) 同步标识
ACK (ACKnowledge Character) 中文译名: 确认字符

建立连接:

  第一次握手: 客户端发syn包(syn=1, seq=随机数), 进入syn_sent状态;

  第二次握手: 服务器收到包,要确认客户的syn(ack=j+1), 同时也发syn包(syn=1,ack=1,seq=seq的基础上再随机数),就是syn+ack包;

  第三次握手: 客户端收到服务器的syn+ack包,向服务器发送确认包(ack=k+1),连接成功

关闭连接:

  第一次挥手: 客户端数据发送完,附带一个fin包(M:序号)给服务器 (潜台词:我想关闭连接了)

  第二次挥手: 服务端收到后,向客户端发送确认序号ack(M+1)   (潜台词: 我知道了)

  第三次挥手: 服务端发fin(N)包给客户端   (潜台词: 我也想关闭连接了)

  第四次挥手: 客户端收到后,发包ack(N+1)给服务端,服务端收到后,close,客户端等,超时后也close

问题1: 为什么要三次握手?两次不行吗?

答: 三次握手的目的是建立可靠的通信信道。直白:双发互相确认对方的发送和接收能力。

  第一次握手: A确认: 无;             B确认: client发, 自己收

  第二次握手: A确认: 自己发&收 ,B发&收;    B确认: A发, 自己收

  第三次握手: A确认: 确认完毕,          B确认: A收 ,自己发, (加上第二次握手,确认完毕)

 

问题2:为什么连接的时候是三次握手,关闭的时候却是四次握手?

C  ---> fin(M) --->  S   我想关闭,你可以吗?

C  <--- ack(M+1) <---  S   我知道了

C  <--- fin(N) <---  S   我可以关闭了

C  ---> ack(N+1) --->  S   你先关,2msl时间后我再关

MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等

----------------------------------------------

摘自网络其他:

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?(不是发完fin包就立刻断开)

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

-----------------------------------------------

问题3:为什么要发送特定的数据包,随便发不行吗?

答:三次握手的另外一个目的就是确认双方都支持TCP,告知对方用TCP传输。

        第一次握手:Server 猜测Client可能要建立TCP请求,但不确定,因为也可能是Client乱发了一个数据包给自己

        第二次握手:通过ack=J+1,Client知道Server是支持TCP的,且理解了自己要建立TCP连接的意图

        第三次握手:通过ack=K+1,Server知道Client是支持TCP的,且确实是要建立TCP连接

2、 TCP和UDP的区别?

  1、tcp是基于连接的,必须先启动服务端,然后再启动客户端去连接服务端

  2、udp是无连接的,先启动哪一端都可以 (应用: QQ聊天)

3、   OSI七层协议是什么?

待续。。。。

 

4、 GIL(Global Interpreter Lock)锁

  python是假的多线程,因为GIL锁的存在,一个进程里面4个线程只有1个线程才能被cpu调用

5、 进程,线程,协程 区别?

1. 进程是一个资源单位(工厂),线程才是cpu上的执行单位。

2. 一个进程下可以有多个线程,多个线程共享该进程的地址空间(资源)。

3. 创建线程开销小,创建速度快

4. 协程:

  在一个单线程里实现并发(IO才有意义)

  修改共享数据不需要加锁

6、  什么是协程?

协程是"微线程",不是真实存在,程序员创造出来的, 功能: 操纵线程在代码之间来回切换执行

  1. 想能更低:如果遇到非IO请求来回切换,性能更低

  2. 性能更高:协程本身不能实现并发,遇到IO(耗时)切换才会实现并发(欺骗cpu)

1. 通过yield实现协程

def func1():    
    print('aaaaa')
    yield 1
    print('bbbbb')
    yield 2
    
def func2():
    print('cccccc')
    yield 11
    
g1=func1()
g2=func2()

g1.send(None)
g2.send(None)
g1.send(None)

2. 通过  greenlet模块实现

from greenlet import greenlet

def test1():
    print 12
    gr2.switch()
    print 34
    gr2.switch()
 
 
def test2():
    print 56
    gr1.switch()
    print 78
 
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

如果 协程+遇到IO(耗时)就切换 => 可以实现并发

7、 并发方案?

  1. 多进程

  2. 多线程

  3. 单线程: 利用 "异步非阻塞"模块 实现单线程并发

单线程:

IO较多的话(不怎么用cpu),虽然可以开多个线程,但是开销仍然可以再小点,所以,可以开一个线程

一个线程把请求发出去,不等,再把其他请求的都发出去....

8、 锁是什么?干什么的?

python里面的锁:

  GIL锁: 保护的是解释器级别的数据  GIL(Global Interpreter Lock)

  Lock锁: 保护的是用户自己的数据

GIl本质是互斥锁

既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

作用:保护不同的数据的安全,就应该加不同的锁。

不完善,待续。。。。。。

9、进程池和线程池?

python2没有线程池

1、进程池

import time
from concurrent.futures import ProcessPoolExecutor


def task(name):
    time.sleep(2)
    print('%s' % name)


if __name__ == '__main__':
    p = ProcessPoolExecutor(4)          # 立刻造好4个进程
    for i in range(1, 11):
        p.submit(task, 'kitty%s' % i)       #submit() 往里面丢任务
    print('主进程')

2、线程池

from concurrent.futures import ThreadPoolExecutor

def func():
    time.sleep(1)


if __name__ == '__main__':
    t = ThreadPoolExecutor()            #创建线程池的时候,如果不设置数量,默认是cpu核数*5
    for i in range(100):
        t.submit(func,)

10、 IO多路复用的作用?

用于监听多个socket对象是否发生变化

import select
while True:
    r, w, e = select.select([sk1, sk2], [sk1, sk2], [], 0.5)

让select模块帮助我们去检测sk1/sk2两个socket对象是否已经发生"变化"(变化只有两个:1. socket连接成功;2.socket得到响应)
    r = []
        如果r中有值
            r = [sk1,]        # 表示: sk1这个socket已经获取到响应的内容;
            r = [sk1, sk2]   # 表示: sk1, sk2两个socket已经获取到响应的内容;
    w = []
        如果w中有值
            w = [sk1,]           # 表示: sk1这个socket已经连接成功;
            w = [sk1, sk2]      # 表示: sk1, sk2两个socket已经连接成功;

注意: w先有值

伪代码:
    for client in w:
        content = “GET /url路径 HTTP/1.1\r\nUser-Agent:....”
        client.sendall(content.encode('utf-8'))

    for cli in r:
        response = cli.recv(8096)
        print(response)
        cli.close()
    IO多路复用, 能监听(感知)请求, 谁连接成功了, 谁收到消息了

IO多路复用的3种实现

select   # 内部循环检测socket是否发生变化;1024个socket
poll    #内部循环检测socket是否发生变化;
epoll  #回调的方式

 

11、什么是异步非阻塞?

非阻塞:  其实就是遇到IO不等待

  拿socket来说:

    sk = socket.socket()

    sk.setblocking(False) # 不等待

  但是会报错,只要捕捉异常,跳过就OK

异步:

  回调: 完成某个功能后(达到某个指定的状态),自动调用某个函数

  例如:ajax请求成功后调用success函数

12、如何自定义异步非阻塞模块?

本质: socket + IO多路复用

  1. 基于事件循环实现

  2. 基于协程实现

以爬虫模块为例:

  爬虫发送Http请求的本质: 创建socket对象

  设置setblocking

  IO多路复用: 循环监听socket是否发生变化,一旦发生变化,可以自定义操作(触发某个函数的执行)

13、HTTP协议的本质?

http协议是建立在TCP协议之上的一个协议,这个协议规定了请求头和请求体之间通过\r\n\r\n分割,请求头之间通过\r\n分割,这是发送的数据的格式。

字符串通过socket发送出去之后,只要拿到相应就断开了,这体现了它: 无状态,短连接。

常用的请求头和响应头:

请求头:
    Accept              # text/html,application/xhtml+xml
    Accept-Charset      # 字符编码  utf-8
    Content-Type        # 设置请求体的MIME类型(适用POST和PUT请求),application/x-www-form-urlencoded
    Referer             # 请求来自哪个页面
    User-Agent      # 用户代理的字符串值

响应头:
    Access-Control-Allow-Origin   # 指定哪些站点可以参与跨站资源共享,   *
    Content-Type        # 响应体的MIME类型
    Set-Cookie      # HTTP Cookie
    Status          # HTTP响应状态

常用的8请求方法:

  ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

状态码:

    200 OK - [GET]:# 服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:# 用户新建或修改数据成功。
    202 Accepted - [*]:# 表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:# 用户删除数据成功。
    
    301     # 永久重定向
    302     # 临时重定向
    
    400 INVALID REQUEST - [POST/PUT/PATCH]:# 用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:# 表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] # 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:# 用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:# 用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:# 用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] # 当创建一个对象时,发生一个验证错误。
    
    500 INTERNAL SERVER ERROR - [*]:# 服务器发生错误,用户将无法判断发出的请求是否成功。

14、浏览器上输入地址,回车,然后发生了什么?(HTTP请求生命周期)

  1. 域名解析:找IP,先本地找,再去域名解析服务器找;
  2. 连接成功;
  3. 浏览器发送数据;
  4. 服务端接收、处理数据并相应;

  服务端做的事比较复杂,如果是Django或Flask。。。(回忆:框架的生命周期)

 

posted @ 2018-05-12 21:33  静静别跑  阅读(166)  评论(0)    收藏  举报