项目八股
项目涉及知识点
C++11新特性:auto类型推导、lambda匿名捕获、范围for循环、智能指针、constexpr、右值引用完美转发移动构造、nullptr
多线程编程:线程互斥、同步、线程池、任务队列
网络编程:http,tcp/ip,epoll/poll/select区别、socket流程,epoll底层实现
linux文件io:
服务器日志:
项目表述
项目是浏览器服务器架构,最基本要求是浏览器发送一个http请求,服务器要能建立起这个tcp连接接收这个http请求并且解析它的内容,然后根据内容进行操作响应,并且要能返回给浏览器请求的结果。实际情况是提出了更高的要求,比如要能够同时监听处理多个请求,用户注册登录查看图片上传下载这些服务器拓展功能,利用一些技术来实现更高的要求。
重难点主要是线程池提交任务设计(包括线程安全的问题),http解析和响应的大段逻辑设计,还有定时器超时跟延时的操作逻辑。
具体来说,首先基于epoll io复用技术使得单个线程能同时监听多个tcp请求,然后引入线程池,将请求任务打包提交给任务队列让工作线程去执行缓解主线程的压力。
对于请求任务,将其分成了解析和响应两个阶段,解析请求通过正则表达式和有限状态机实现,正则表达式在解析请求行、请求头请求体过程中负责匹配关键字符获取到例如请求类型请求url,content length等等,有限状态机负责在每一次进入解析循环中在不同状态切换跳过已经解析的阶段,使得结构上紧凑。
设计解析代码的时候需要针对不同的请求可能请求的内容都进行设计,比如在我的项目里用到了POST请求跟GET请求,这个POST请求就用于用户注册登录以及上传文件,对于第一种,用户注册登录,请求体里会有key=value这种键值对内容表示用户名跟密码,解析得到了这个需要调用数据库进行增加查找操作。对于第二种,上传文件,解析时只需要它的名称部分,那请求体的内容需要用一个额外的string存储,再利用文件流将其写入到磁盘上。
GET请求
响应阶段采用将响应结果和响应的内容写入socket的发送缓冲区进行传输,响应结果就是按响应状态行,响应头,响应体的顺序写,响应的内容写入发送缓冲区。响应内容对于客户端看到的东西影响是最大的,浏览器接收到响应后,会根据响应头中的内容类型(Content-Type)确定如何处理响应体。并发请求并发解析渲染都是浏览器本身在完成,服务器响应体写入的代码逻辑需要根据请求内容进行返回。
数据库优化,缓冲区优化,心跳检测,
基于Epoll与线程池的实现单Reactor多线程的并发服务器
基于阻塞队列、互斥锁、条件变量实现线程安全的异步的日志系统,记录服务器运行状态与异常
通过正则表达式与有限状态机解析HTTP请求报文,可同时处理GET与POST请求,结合MySQL实现用户注册登录
能够处理浏览器发送的multi/form-data类型的post请求,实现了文件上传功能
通过jsoncpp生成json数据,向浏览器前端发送服务器本地文件列表,实现文件展示与下载
内存优化:底层利用Vector结合临时栈空间实现自动增长缓冲区改善内存利用情况
连接优化:基于小根堆实现的TCP定时器,关闭超时的非活动连接,提升服务器性能
1.介绍一下IO多路复用,select epoll poll有什么区别 为什么用epoll
IO多路复用指的是让单一线程能
2.线程池是怎么实现的
任务是怎么提交的,bind函数绑定的是函数指针,如果是类的成员函数,没办法隐式转换为函数指针,所以需要手动取地址,同时需要传实例对象指针不然找不到这个成员函数。如果是绑定普通函数则不用显式转换为函数指针。
3.什么是Reactor、Proactor、为什么是单Reactor多线程,项目是怎么设计的。
4.锁是怎么保证阻塞队列安全的。
项目八股:
OSI七层模型:
应用层
会话层
表示层
传输层
网络层
数据链路层
物理层
为什么需要TCP协议?
TCP也好、UDP都是传输层的协议,这样的协议定义了数据传输过程的规范,光是IP协议是不可靠的,IP协议只指明了需要送达的一个大体地址,但是没有指明需要接收的端口,因而无法保证数据能传到目标应用程序。TCP连接的四元唯一性可以保证连接是一对一的送达。
除此之外TCP协议是面向字节流的。能保证数据是有序的,因为TCP头里有序列号跟期望响应号,能保证按顺序的接受处理给应用程序。
最后TCP协议还进行了拥堵控制(TCP滑窗)
最大连接数是多少?如果是从固定了服务器监听的某一个端口来说,那么理论上界为用户的IP数x端口数
实际上限会受到系统、用户、进程能打开的最大文件描述符数量限制 不过这个限制能够突破 ulimit -n
以及每创建一个新的tcp请求,需要为其分配一个新的connect socket进行监听读写对端关闭事件,会需要为其在内核维护读写队列,分配内存。
解释一下TCP、UDP区别
TCP如上,udp协议的设计相比于TCP是不够可靠的,它是尽量将数据送达,但不保证交复质量。
UDP的协议头里只有端口号校验码包长度等等信息。
TCP是面向连接的,udp是无连接的,TCP进行传输之前要三次握手建立连接保证双方都具有发送和接收的能力,而UDP直接传
TCP有流量控制 UDP没有
TCP协议头比较长。UDP协议头短
TCP是面向字节流的传输,会需要解决数据边界的问题,而UDP是一个包一个包发送
TCP分片组组装是在传输层,而UDP分片组装是在网络层。
TCP UDP 应用场景:
TCP适合传文件,不要求实时性的网络通信
UDP适合传视频音频聊天会话之类的 对实时性有一定效率要求的场景
解释一下tcp三次握手四次挥手
tcp三次握手才建立起一个全连接的原因是客户端和服务器端互相知晓对方的收发能力需要时间验证。
服务器端要建立对某个tcp传输请求的监听,需要配置创建socket文件,设置ip地址等一些参数,之后绑定端口号,进入listen状态。
等待客户端发送connect请求,accept请求开始建立connect socket,三次握手完毕后返回fd。供后续读写对端关闭等操作。
第一次客户端发送syn报文,随机初始化序列号,客户端进入syn-sent状态。
第二次服务器接收到客户端发来的syn报文,服务器端会随机初始化一个服务器序列号,将客户端发来的客户端序列号+1作为ack应答号,发送syn+ack报文给客户端,服务器端进入了syn-rece状态,服务器端已经知晓了客户端的发送能力。
第三次握手是客户端接收到服务器发来的报文,客户端知晓了服务器端的收发能力,但是服务器端却不知道客户端能不能接收。此时客户端需要将受到的报文中的应答号再+1发送给服务器端。进入建立连接状态。服务器端受到报文,也进入建立阶段,此时tcp连接建立成功。
三次握手完成了socket的初始化,序列号确认收发能力。
三次握手的最主要目的是防止过去重复的连接将这次连接进行了初始化,避免发了一堆数据是白发的,保证建立的连接是针对当前需要传输的内容。
为什么每一次随机初始化的序列号都是不一样的,是为了防止混入了历史报文的内容,造成建立的连接是针对历史请求的。
握手迟迟没有收到应答报文会触发tcp的超时重传,重新发送syn+相同的序列号。
超时重传:第一次握手报文丢失了,会触发客户端重新发送syn报文,序列号一致
第二次握手报文如果丢失了,客户端角度会以为第一次没发出去,就再发一次syn加相同序列号,服务器端角度会以为自己的syn+ack报文没发出去,也会重发。
第三次握手如果超时了,服务器端会再次发送syn+ack报文。
讲一讲TCP四次挥手断开连接:
建立连接是从客户端发起connect请求开始的,而断开连接,双方都可以发起。
TCP的重传机制:超时重传,如果到达了往返时间,但是仍未受到返回的信息,就再次发送。
快速重传:客户端连续发好几个数据,但是服务器给他返回的是三个相同的ACK报文,这时候根据报文的值就知道是哪一个数据没送达,重新发送。但是实际情况往往不止丢一个,可能丢好几个报文,重传无法确定是哪一个报文丢了,而重传所有报文又太浪费资源了。
选择性确认:接收端在ACK报文里可以将已经受到的数据的信息发送给发送方,这样就可以只重传丢失的数据。
发生读事件时函数的调用栈是怎样的。
多进程多线程
锁
IO复用
设计模式
工厂模式:简单工厂。。。
观察者模式:
单例模式:
进程pid
进程pid(进程ID),每个进程在系统中都有一个唯一·的非负整数表示的进程ID,用getpid() 获取进程ID。
线程tid
线程tid(线程ID),每个线程在所属进程中都有一个唯一的线程ID,用pthread_self() 获取自身现成ID。有多个进程时,可能会出现多个线程ID相同的线程,故线程tid只在其所属的进程上下文中有意义,不能作为系统中某个线程的唯一标识符。
线程pid
线程pid,每个线程在系统中都有一个唯一的pid标识符,用系统调用sys_call(SYS_gettid()) 获取自身线程pid。主线程pid与所在进程pid相同。
浙公网安备 33010602011771号