网络编程,从socket到epoll
参考链接:https://www.bilibili.com/video/BV11Z4y157RY?p=2&spm_id_from=pageDriver
socket基本知识:
socket分类:
socekt提供了流和数据报两种通信机制,即流socket和数据报socket。
简单的socket通信流程:
先建立通道——>再收/发报文(使用recv函数和send函数进行收/发报文)。
一个服务器有两个网端(网卡),但是左边的两个ip地址只能通过192.168.10.10进行通信。
相关的库函数:
recv()函数:是等待。
close(socket)关闭资源。
AF_UNIX()函数。
ip地址的范围:0.0.0.0~255.255.255.255。
ipv4和ipv6的区别,目前更常用的是ipv4。
socket的参数:socket(AFINET,SOCK_STREAM,0)。
面试问题:
在一个程序里面最多打开多少个文件数?答案:1024个。可以搜索“linux一般打开文件的数量”。
结构体(重要):
socket结构体:
hostent结构体:
两个函数:
字符串ip地址和网络字节序ip地址相互转换。网络字节序ip地址是整数int类型。
网络字节顺序:
参考链接:https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E5%AD%97%E8%8A%82%E5%BA%8F/12610557
主机字节序
主机字节序,即CPU存储数据时采用的字节顺序。不同的CPU设计时采用的字节序是不同的。平常大多数PC与服务器如果使用的是Intel与AMD CPU,一般都是little endian。
参考链接:https://blog.csdn.net/K346K346/article/details/79053136
设置服务器socket的SO_REUSEADDR属性
listen()、connect()和accept()函数
listen的socket队列:
TCP的三次握手:
send()函数:
返回已发送的字符数。
RECV函数:
用于接受对端通过socket发送过来的数据。应用程序都用recv函数接收来自tcp连接的另一端发送过来的数据。
TCP报文分包和粘包
多进程网络服务端框架freecplus框架
参考链接:https://www.freecplus.net/9ebb8276265b404488a3415be224ed85.html
Writen和Readn
两个函数readn和writen的功能是读、写指定的N字节数据,并处理返回值小于要求值的情况。这两个函数只是按需多次调用read和write直至读、写了N字节数据。
参考链接:https://www.cnblogs.com/nufangrensheng/p/3559381.html
I/O复用,之后讲
孤儿进程和僵尸进程
定义:
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
僵尸进程解决办法:
(1)通过信号机制
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
(2)两次fork
将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程(进程号是1)可以处理僵尸进程。
参考链接:https://www.cnblogs.com/anker/p/3271773.html
C语言程序运行日志
关注点有:
程序执行的过程
处理数据的情况
记录异常和错误
程序的调试
日志文件的基本操作:
类的声明
打开日志文件
写入日志文件
write写入时间,writeex不写入时间。
关闭日志文件
目录操作:
文件操作:
多进程和多线程服务程序的日志:
使用同一个日志,可能会产生竞争。
多进程序的日志文件不能切换。
如果使用同一个日志文件,用锁太麻烦,还会影响程序的效果,一般分开写日志。
进程间通信:
进程间通信包括:
1)数据传输
2)共享数据
3)通知事件
4)进程控制
进程间通信方式包括6种:
1)管道:有无名管道和命名管道。
2)消息队列(message)
3)信号(signal)
4)共享内存(shared memory):多个进程可以访问同一个进程空间。
5)信号量(semaphore):也叫信号灯,用于进程之间对共享资源进行加锁。
6)套接字(socket)
在企业IT系统内部,消息队列已经逐渐成为通信的核心手段,它具有低耦合、可靠投递、广播、流量控制、一致性等一系列功能。当今有很多主流的消息中间件:Redis、Kafka、RabbitMQ、ZeroMQ,阿里巴巴自主研发RocketMQ等。
管道和消息队列不能用于多台服务器之间。
参考链接:https://www.bilibili.com/video/BV1YD4y127aM?from=search&seid=9253392497960943444&spm_id_from=333.337.0.0
共享内存:
参考链接:https://www.bilibili.com/video/BV1YD4y127aM?p=2&spm_id_from=pageDriver
共享内存的操作:
相关函数:
shmget函数:创建共享内存。先创建共享内存,创建不成功才再尝试获取(和semget函数相反)。
shmat函数:连接
shmdt函数:共享内存从当前进程中分离
shmctl函数:删除共享内存
其他的操作命令:
ipcs -m 可以查看系统的共享内存。
ipcrm -m共享内存编号,可以删除共享内存编号。
nattch:
二元信号量:
参考链接:https://www.bilibili.com/video/BV1YD4y127aM?p=3&spm_id_from=pageDriver
信号量只允许进程进行等待信号和发送信号。
二元信号量是最简单的信号量。
ipcs -s:查看信号灯。
ipcrm sem 425985:删除信号量425985
oracle信号量删除不了的。
相关函数:
semget:创建信号量。先获取,如果不存在,再去创建。
获取信号灯,如果信号灯不存在,创建它。信号灯创建成功后,还需要初始化为可用的状态
semctl:删除信号灯。
semop:是一个结构体,等待( wait() )信号量。 op减一
semop:是一个结构体,释放( post() )信号量。op加一
利用信号量给共享内存加锁:
主要就是把信号量和共享内存的代码结合在一起即可。
通用信号量和信号量集:
给多进程的socket服务器增加业务逻辑:
补充:C++知识
C++重载:参数列表不同。函数重载仅仅是语法层面的,本质上它们是不同的函数,占用不同的内存,入口地址也不一样。
C++引用:就是变量的别名,对引用的操作和对变量直接操作完全一样。
参考链接https://www.bilibili.com/video/BV1tp4y1i7rL?p=82
C++运算符重载:如果类重载了==运算符,那么就可以用==判断两个类是否相等。运算符重载的实现是通过operator关键字实现的。
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=84
C++动态内存管理:有new和delete
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=88
C++类的继承和派生:当创建一个类时,如果待创建的类与另一个类存在某些共同特征,程序员不需要全部重新编写成员变量和成员函数,只需要指定继承另一个类即可,被继承的类称为基类或者父类,新建的类称为派生类和子类。
定义派生类的语法:
子类没有的函数,就会调用父类的函数。如果子类和父类都有相同的函数,是调用了子类还是父类的函数呢?
答:是调用子类的。
基类的指针可以指向基类对象,也可以指向派生类对象,但是不能通过基类的指针访问派生类的成员。
派生类的指针可以指向派生类对象,但是不能指向基类对象。
C++多继承:C++类可以从多个基类继承成员,语法如下:
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=89
C++类的多态:多态按照字面意思就是多种形态。当类之间存在继承关系时,就可能会用到多态,调用成员函数时,会根据对象的类型来执行不同的函数。
静态多态:函数重载就是一个简单的静态多态。
虚函数:是在基类中使用关键字virtual声明的函数,在派生类中重新定义虚函数。
在程序中可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态绑定,或后期绑定。
纯虚函数:在基类中只是申明虚函数,没有函数的定义,在派生类中去实现函数的定义,这个时候就会用到纯虚函数。
C++接口(抽象类):接口描述了类的行为和功能,不需要累的功能实现。C++接口是用抽象类实现的。如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。
可用于实例化对象的类被称为具体类。
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=90
抽象类不能直接通过new去实例化一个对象,那它就是不能实例化,要获取抽象类的对象, 需要先用一个类继承抽象类, 然后去实例化子类。
参考链接:https://cloud.tencent.com/developer/article/1448100
对socket通信的函数进行封装:主程序代码太多,可以分离出去,采用的方法就是封装。
客户端连服务器的函数,可以封装服务端ip和通信端口。
函数的声明:
函数的实现:
构造函数和析构函数:
构造函数是一个特殊的公共成员函数,它在创建类对象时会自动被调用,用于构造类对象。构造函数不允许有返回类型。
析构函数是具有与类相同名称的公共成员函数,前面带有波浪符号(〜)。
参考链接:http://c.biancheng.net/view/1401.html
C++封装的意义:
1)把数据初始化的代码放在构造函数里中;
2)把关闭socket等释放资源的diamante放在析构函数中;
3)把socket定义为类的成员变量,类外部的代码根本看不到socket。
4)代码更简洁,更安全。(析构函数自动调用并关闭socket,释放资源)
Linux的多进程:
进程的概念:是针对系统不是针对程序员。对于程序员来说,面对的概念是程序,当输入指令执行一个程序的时候,对系统而言,它将启动一个进程。
进程就是正在内存中运行中的程序,linux下一个进程在内存里有三个部分的数据,就是“代码段”、“堆栈段”和“数据段”。
“堆栈段”存放的是程序的返回地址、程序的参数以及程序的局部变量;“数据段”存放程序的全局变量、常数以及动态数据分配的数据空间(比如用new函数分配的空间)。
系统如果运行相同的程序,“堆栈段”和“数据段”是相同的吗?
答:不同(相同的程序,处理的数据是不同的)。
进程的命令:
命令 ps 查看进程
ps -ef 查看系统全部的进程
ps -ef|more 查看系统全部的进程,结果分页显示
ps -ef|grep :grep是搜索我们需要的进程。
getpid()获取本程序运行时进程的编号。
注意的细节:进程的编号是系统动态分配的,相同的程序在不同的时间执行,进程的编号是不同的。进程的编号会循环使用,但是,在同一时间,进程的编号是唯一的,也就是说,不管是任何时间,系统不可能存在两个编号相同的进程。
多进程:
fork():fork()产生一个新的进程,函数返回值pid是一个整数,在父进程中,返回值是父进程的编号,在子进程中,返回值是0。
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=99
多线程:
pthread_create:创建线程
pthread_exit:线程终止。还有另外两种终止方法。
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=109
查看线程:
top -H
ps -xH|grep book261
线程同步:
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=111
日常生活中,锁有两种,一种是不允许访问,另一种是资源忙,同一时间只允许一个使用者占用,其他使用者必须等待。
线程的锁的种类有:互斥锁、读写锁、条件变量、自旋锁
信号灯。
互斥锁:互斥机制是同一时刻只允许一个线程占有共享的资源。
互斥锁的过程:初始化、阻塞加锁、非阻塞加锁、解锁。
C/C++程序中调试可执行程序:
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=112
exec函数族:
exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行的脚本文件。
参考链接:https://baike.baidu.com/item/exec%E5%87%BD%E6%95%B0%E6%97%8F/3489348?fromtitle=EXEC&fromid=9077756
system函数:
提供了简单的执行程序的方法,把需要执行的命令用一个参数传给system函数。
Linux的静态库和动态库
参考链接:https://www.bilibili.com/video/BV1tp4y1i7rL?p=113
库文件和其它的源文件一起编译,是静态编译;库文件被操作系统载入内存执行是动态编译。
.a是静态库,.so是动态库。
静态库的优缺点:
动态库的优缺点:
https://cloud.tencent.com/developer/article/1448100
XML实战应用之精髓
生成和解析xml字符串
xml应用的三个方面:
1)解析xml数据文件
2)xml用于网络通信
3)xml用于程序的参数
程序运行,参数非常多,一个个写非常复杂,并且很容易出错。所以应用xml,把参数通过xml传递,然后再解析xml即可。
4)把简单的xml发挥到极致:数据交换、数据同步。提高工作的效率。
参考链接:https://www.bilibili.com/video/BV15v411q7Yb?p=6
增加业务逻辑:
GetXMLBuffer()函数
TCP短连接与长连接
长连接的心跳机制:
心跳机制:就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。
参考链接:https://blog.csdn.net/aspnet_lyc/article/details/37318861
心跳报文:<bizcode>0</biz000>
搭建多线程网络服务器框架:
参考链接:https://www.bilibili.com/video/BV11Z4y157RY?p=29
https://www.bilibili.com/video/BV1zf4y1Q7Nj?p=12
性能测试的重要性:
测试服务端并发性能:
测试服务端业务性能:
多进程和多线程服务端性能差异:
mpserver:多线程
mtserver:多进程
在测试业务性能方面:多线程占用CPU和内存上面,更有优势。
参考链接:https://blog.csdn.net/lishenglong666/article/details/8557215
多进程和多线程如何选择?
1)需要频繁创建销毁的优先用线程
2)需要进行大量计算的优先使用线程
3)强相关的处理用线程,弱相关的处理用进程
4)可能要扩展到多机分布的用进程,多核分布的用线程
参考链接:https://blog.csdn.net/lishenglong666/article/details/8557215
测试客户端的响应时间:
测试网络带宽:
I/O复用介绍
select、poll、spoll
参考链接:https://www.cnblogs.com/yungyu16/p/13066744.html
Select:
Select的流程:
Select的超时机制:
应用fd_set实现seclect的超乎机制。
select模型会丢失事件和数据吗?
答:不会。select采用水平触发的方式,如果报告fd后事件没有被处理或者数据没有被完全读取,那么下次select时会再次报告该id,也就是说select不会丢失事件和数据。
参考链接:https://www.136.la/jingpin/show-204521.html
select缺点是:
pselect函数可以了解一下。
select优势是:
实现跨平台,时间精度高(ns级别)
select的基本流程:
参考链接:https://www.cnblogs.com/yungyu16/p/13066744.html
位图Bitmap的原理:
poll模型的原理和实现:
poll概念:
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
参考链接:https://www.cnblogs.com/yungyu16/p/13066744.html
参考链接:https://www.bilibili.com/video/BV11Z4y157RY?p=44
poll函数和参数:
epoll模型的原理和实现:
epoll模型的函数和参数:
epoll_create
epoll_ctl
epoll_wait
参考链接:https://www.bilibili.com/video/BV11Z4y157RY?p=45&spm_id_from=pageDriver
参考链接:https://www.cnblogs.com/yungyu16/p/13066744.html
epoll的水平触发和边缘触发:
事件没有全部被处理或者数据没有被全部读取,epoll会在下次报告给fd,那么就是边缘触发。如果立即报告给fd,就是水平触发。
epoll应用场景:
- 对于连接特别多,活跃的连接特别少
- 典型的应用场景为一个需要处理上万的连接服务器,例如各种app的入口服务器,例如qq
参考链接:https://www.cnblogs.com/yungyu16/p/13066744.html
IO五种模型:
参考链接:https://www.136.la/jingpin/show-204521.html
1)阻塞IO
最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
2)非阻塞IO
3)IO多路复用
4)信号驱动IO
5)异步IO
I/O复用中的写事件:
I/O复用的其它知识:
参考链接:https://www.bilibili.com/video/BV11Z4y157RY?p=48&spm_id_from=pageDriver