/Network_programming

c++高并发网络编程项目

Primary LanguageC++

c++高并发网络编程 项目笔记

高性能服务器基本框架

image

1.Socket 编程

  • 服务端:1.创建socket() 2.绑定端口 bind() 3.监听端口 listen() 4.接受连接 accept() 5.接收数据 recv() 6.发送数据 send() 7.关闭端口 close();
  • 客户端:1.创建socket() 2.连接服务器 connect() 3.接收数据 recv() 4.发送数据 send() 5.关闭连接 close()

2.非阻塞网络模式 select模式

四个基本函数:

  • FD_SET(_sock,&fdReads):将_sock加入待观察数组fdReads
  • FD_CLR(_sock,&fdReads):将_sock从待观察数组fdReads中移除
  • FD_ISSET(_sock,&fdReads):判断_sock是否有待处理时间
  • FD_ZERO(&fdReads):将待观察数组fdReads清空 fdReads数组实现方式在windows和linux中有所区别,windows是由数组中元素个count及数组array组成,linux中由指针实现

select模式原理:

函数参数:select(max_sock+1,&fdReads,&fdWrites,&fdExcept,flag)。轮询检查对应数组中的文件是否有待处理文件,有则将对应文件标识符值为true,从而实现非阻塞网络模式。在windows中最大文件数为64(可通过修改FD_SETSIZE这个宏定义来修改可支持的最大文件数),在linux中最大文件数1024(无法修改,若要突破这个值,只能使用epoll模式),这也是select模式中网络的最大连接数(注:linux中的select有巨大的坑,即使是使用多线程也无法突破最大连接数,多进程可以,暂时无法解决,先做个标记)。

  • linux 下的文件描述符(file descriptor) 一个linux进程启动后,会在内核创建一个进程控制块(PCB process Control Block),PCB内部有一个文件描述符表(File descriptor table),文件描述符(索引)就是文件描述附表这个数组的下标,数组的内容就是指向一个个打开文件的指针(程序对文件的所有操作都依赖这个指针)。同时还规定系统刚启动时,0是标准输入,1是标准输出,2是标准呢错误,这意味着如果打开一个新文件,描述符是从3开始的,并依次递增...

3.粘包,少包

  • 产生原因:高吞吐量下服务器缓存已经占满,服务器处理速度跟不上接收信息速度。
  • 解决方案:增加二级缓存。

4.多线程基本概念

  • 临界资源 锁的概念 c++中 mutex 库 .lock() 和 .unlock成员函数锁定了临界区域 mutex库中lock_guard类已经将上述两个成员函数写进构造和析构函数,只要将临界区域的代码写在一个{}域中,推出时能自动调用析构函数
  • 原子操作:cpu运算中不可分割的操作,一代开始除非全部完成操作,否则在cpu中不会中断来执行其他线程,其中原子操作的cpu消耗比加锁要低
    c++中 atomic 库
  • 线程休眠函数:std::chrono::milliseconds t(1) :先定义chrono库中的休眠时间
    std::this_thread::sleep_for(t) :该线程休眠t时间
  • 线程同步原语:头文件 #include<condition_variable> condition_variable对象可用来阻塞多个线程,但必须配合std::unique_lock使用,代码示例:
    unique_locklock(m);
    cv.wait(lock) //自动解锁lock,并将当前线程挂起(阻塞)
    或者 cv.wait(lock,[this]{return condition;}); 这种情况下只有后面的condition为false时才会被阻塞,只有当条件为true时才会被唤醒。 ......
    等待其他线程使用cv.notify_all()来解开所有被挂起的线程

5.C++一些基本概念

三大特性:封装,继承,多态

  • 虚函数:虚函数可以继承(除构造函数外),虚函数使得基类的指针可以调用派生类的成员函数(前提:1.当基类指针指向派生类对象时 2该成员函数继承自基类,其重载了基类的成员函数。)
    纯虚函数:void fun() = 0; 不具备函数的功能,不能被调用,它只是通知编译器这里声明一个纯虚函数,留待派生类中定义
  • 私有成员 子类(派生类)可使用继承制父类的方法操作私有成员,但不能使用自己重写的方法来操纵私有成员,但可以操纵protected成员

C++中头文件和源文件

  • C++语言支持“分别编译”,一个程序的内容可以放在不同的.hpp文件中,它们是相对独立的,在编译过程中不与其他文件相通。只有在链接(link)过程中,主程序会根据.h文件找到编译好的函数,将它们链接成一个程序。(注:函数的声明和定义最好分别放在.h文件和.cpp文件中,因为声明可以被多次导入,但是定义最多只能被导入一次,否则会造成函数重定义的问题。这两个文件可以不同名,但是推荐最好同名)

C++中的单例模式

  • 单例模式就是一个类只能被实例化一次,更准确的说是只有一个实例化的对象的类,单例能起到保护类内对象的作用。
  • C++中的实现方式: 通过将构造函数私有化,再使用类内的静态成员函数返回一个静态对象来调用其他的成员函数。

结构体对齐方式

  • 64位程序和32位程序中,指针分别为8字节和4字节。
  • 结构体对齐遵循两个原则:1. 每个成员按照类型的大小对齐(相对于结构体地址的成员地址能被类型大小整除) 2.结构体作为成员,类型大小按其成员所含最大类型计算。

assert断言

  • 作用:在函数开始处检验传入参数的合法性 用法:assert(condition),若条件不满足终止执行程序。

6.git的一些操作

git的一些基本操作

  • git add filename : 将某个文件提交到暂存区
  • git commit -m "修改注释" : 将修改代码提交到本地仓库
  • git push : 将代码提交到远程仓库
  • git log : 显示所有修改的版本记录
  • git reset -- hard 版本号 : 代码回退到某个版本号 git reset --hard HEAD^ : 回退到上个版本的代码

git分支管理

  • git branch : 显示所有当前分支
  • git branch dev : 创建该名字的分支
  • git push origin dev 或者 git branch --set-upstream-to=origin/dev : 将当前新创建的分支更新到远程仓库
  • git branch -d dev : 删除dev分支
  • git push origin :dev : 删除dev分支,前面的冒号表示删除
  • git checkout dev : 将当前默认分支转换到新的分支
  • git merge dev : 前提是先切换到主分支,再将某个分支的代码合并到主分支

7.TCP三次握手和四次挥手

image

基本概念

  • 2个序号:
    1.seq(sequence number) : 顺序号
    2.ack(acknowledge number) : 确认号,响应前面的seq,为接收的seq值+1。

  • 6个标记位:
    1.URG(urgent) : 紧急标志
    2.ACK(acknowledgement) : 确认标志
    3.PSH(push) : 表示推送操作
    4.RST(reset) : 重置复位标志
    5.SYN(synchronous) : 发送/同步标志
    6.FIN (finish): 结束标志

  • Dos攻击
    客户端与服务端建立连接要发送三个报文,如果客户端通过伪造不存在的IP作为源地址向服务器发送SYN连接请求报文,服务端收到后回应一个SYN+ACK报文并在自己的半连接队列中为收到的SYN报文创建一个条目并等待客户端的回应。但客户端采取了IP欺骗,服务器发送的SYN-ACK报文根本得不到回应,这时服务器会不断等待、重传直至重传次数超过系统规定的最大重传次数才停止,并将这个SYN项目从半连接队列中删除。SYN泛洪攻击就是在短时间内伪造大量不存在的IP地址并快速发送大量这样的SYN报文给攻击目标计算机,使其半连接队列被阻塞,正常的SYN请求反而被丢弃,同时还要不断对这个庞大的半连接队列中所有项目进行SYN+ACK的重试,系统可用资源急剧减少,系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。

  • 为什么TCP连接要三次握手而关闭要四次挥手?
    在关闭连接时,收到主动方的报文只表示对方没有数据发送给你了,但是可能被动关闭连接的一方任然存在要发送给对方的数据,这时候被动关闭的一方会马上发送一条ACK数据向对方确认已经收到FIN报文,然后等待数据发送完成后再发送SYN同步报文。而TCP连接建立的过程中并不存在这样的情况,相当于服务器端一次性将ACK确认信息和SYN同步信息放在了同一个报文中发送给了对方。

8.内存管理

  • 目的:避免内存随便的产生,是程序长期稳定、高效的运行

内存分配的三种方式

  • 1.内存池:从系统中申请足够大小的内存,由程序自己管理
  • 2.对象池:创建足够多的对象,减少创建释放对象的消耗
  • 3.智能指针:保证被创建对象的正确释放 (主要是为了避免内存泄漏)