服务器开发准备阶段
TCP/IP
TCP/IP部分 参考笔者拙文:一小时通关计算机网络( 冲冲冲!!!)
伯克利 socket API
server:
bind
listen
write
close
client:
connect
read
close
安装 Linux Ubuntu (虚拟机)
下载虚拟机
https://www.vmware.com/jp/products/workstation-player/workstation-player-evaluation.html
下载 ubuntu 系统
https://ubuntu.com/download/server
http://mirrors.melbourne.co.uk/ubuntu-releases/
安装 ubuntu 系统
XShell
下载:
https://www.netsarang.com/zh/xshell/
使用 Xshell 连接远程服务器
注意:XShell 连接不上服务器的解决办法
- 服务器未安装 ssh 工具 或者 ssh 服务未开启
解决办法
- 检测 ssh server是否启动:ps -e | grep ssh
- 先更新软件源: sudo apt update
- 下载ssh 工具: sudo apt-get install -y openssh-server
- 网段不同
解决办法,在虚拟网络设置 虚拟网络8 的网关地址与 虚拟机 ip 同一网段
3. 重新默认配置
Xftp (远程文件传输)
XFTP: XFTP 是一个远程文件传输的工具,主要用于将本地文件传输到 终端上
服务器开发所用到的 Shell 命令
通用命令
开机准备:
who
: 查看现在登陆的用户信息sudo (最高权限) apt-get update / upgrade / dist-upgrade
: 检测系统更新/ 软件包更新 / 大版本更新sudo add-apt-respository ppa:ubuntu-toolchain-r/test
: 更新Ubuntu环境下的最新源- 换网络点(提高下载速度)步骤
wget -c (网点 : http://mirrors.163.com/.help/sources.list.trusty) :
下载网点配置cp sources.list.trusty
. : 拷贝配置源文件到当前目录(/etc/apt)cd /etc/apt , cp sources.list sources.list.bak
备份 原配置文件cp sources.list.trusty sources.list
:替换原配置文件
查看:
- man (Manual) pwd :查看 pwd 指令的文档
- pwd : 查看当前目录连接
- ls :查看当前目录下的文件
ls / :查看根目录文件
可选择: (-la 详细信息查看,a 查看其 连接计数)
*: 代表了可查找的信息所有可替换,? : 代表了信息的单个替换 stat aaa.txt
:查看文件属性file abc.txt
:查看文件类型cat
:输出文件内容tail (-f, -n + 行数)
:查看尾部一定行数的内容
切换目录:
- cd :切换目录
- cd / :切换根目录(绝对目录)
- cd (不加 /) 相对目录
- cd … :返回上一目录
创建:
touch abc.txt
:没有则创建文件,有则修改文件时间mkdir abc
:创建目录- ln :创建链接命令
拷贝
cp aaa.txt aaa.bak
:拷贝cp hzj.txt hzj.txt
:替换
-i (询问) -l (硬连接) -s (软连接,相当于别名,跨硬盘,删除指向文件,软连接失效)
删除
rm hzj.txt
:删除文件(不能删除目录)rmdir hzj
: 删除目录(目录有文件不能被删除)rm -r doc
: 递归删除目录(包括目录中的文件)
修改
mv hzj.txt hzj.txt.1
:移动文件(修改文件名字)
查看系统上的命令
- ps : 输出当前用户的进程
(-ef)(al) :显式全部进程详细的信息 - top :实时显示进程信息,系统信息
- htop : 带图形化的 top
下载: sudo apt-get install htop (远程服务器下载) - ps aux| grep top :列出 top 进程的 aux 序号等信息
- kill -s(-q :强制结束) INT aut序号 : 给该程序发送结束信号
- df -h :查看当前盘的使用情况
其他杂项命令
-
grep (global re(正则表达式) print) :查看服务器日志
-
grep apple(re表达式) hzj.txt : 在文件中查找 apple 所在的行,并输出其行内容
(-c : 输出行 -v :输出不出现给顶表达式的行内容 -n : 输出出现的行号与内容)
作为管道右侧: ps | grep bash (将 | 管道左侧的输出作为右侧语句的输入) -
zip :压缩
-
unzip :解包
-
tar
-
wget (-c :下载中断时,下次继续在此继续下载) link(链接)
-
echo $PATH : 打印(其环境变量)
-
printenv :输出所有环境变量
-
chomd +x (-x) hzj.txt :改变文件权限
-
ctrl +z :暂停任务
-
jobs :查看当前任务
-
fg + 任务序号 :恢复任务
-
scp 用户名@主机:/home/handling/main.cpp :内网主机传输文件:
简易 vim
vim 初学者命令
vim 默认是认为我们是只看不编辑的,所以是 命令模式 Normal
当我们键入命令,就能执行对文本的操作:
模式切换:
- ESC :退回到 Normal 命令模式
- 命令模式 v : Vitrual模式,移动光标选择范围,运行命令针对选项范围。
- : 冒号键入命令
插入命令
- i 插入光标之前
- a 插入光标之后
- o 在当前行下插入新行
移动光标:
-
h : 左移
-
j : 下移
-
k : 上移
-
L : 右移动
-
w : 到下一个单词开头
-
e : 到下一个单词结尾
-
^ : 移动行首非 制表符的字符
-
g_ : 移动行尾 非制表符的字符
-
0 : 行首
-
$ ; 行尾
-
NG : 跳转到第 N 行
-
gg : 到第一行
-
G : 跳转文本最后一行
-
% : 光标移动到 与之匹配的括号
-
*与# : 匹配当前光标所在的单词, * 是移动到下一个匹配,#是上一个
选择区块:
Visual 模式
- vi “ : 选择两个 ”“间不包括 引号的内容,
- va" ; 包含 ”
- vi ) :选择 () 内容,不包含 ()本身
- va ) : 包含 ()
ctrl + v 进行块操作,选中块,如果需要插入则 设置大写 I ,进行多行插入,之后 esc 恢复 normal 状态插入成功。
拷贝与粘贴:
- YY : 拷贝当前行
- p : 粘贴寄存器的字符行
取消操作与重复操作:
- u :取消上一次操作
- ctrl + r :重复取消操作
- . : 重复上一个改变文本的操作
查找:
- / 查找的文本(RE) :向下查找
- ? 查找的文本(RE) : 向上查找
- n :跳到下一个为止, N :跳到上一个查找位置
- fh : 行查找下一个字符h出现的位置,F反向
- th : 到下一个 h 前的字符位置, T反向
删除:
- x 删除当前光标字符
- dd :删除光标所在行
- dt“ : 删除从光标开始的行的内容,直到遇见 “
自动补全:
Insert模式
- (ctrl + n 或 ctrl + p):能选择匹配到的单词
自动缩进:
- = :选中区块自动缩进
多行连接:
- J :选中行连接
分屏操作:
- vsplit : 分屏
- ctrl + w +w :光标多屏跳转
- hide :隐藏光标所在屏
- b main.h :同时编辑文件时将光标所在屏转到 main.h
退出:
- wq : 保存退出
- q!:强制退,不存盘
- ZZ : 存盘退出
环境变量:
:set hlsearch :高亮查找
:set number : 显式行号
帮助
- help :vim 使用规则
- vimtutor :专门介绍 vim 的使用规则
vim 配置文件与环境变量
vim 环境变量配置在用户目录的 .vimrc中
环境变量配置:
服务器开发初始阶段
深入学习 C++
准备: 配置环境
Gcc下载: sudo apt-get install g++
make : 工程管理
cmake :更高的工程管理
关键字:KEY-WORDS
alignas 与 alignof (c++11):
alignas() 是能够显式设置其对齐字节数的。//默认设置为 最大成员字节数为对齐字节数
这种明确规定占用字节大小后,编写代码将更具有跨平台性。
而 alignof 是查看当前对象的 对齐字节数
struct alignas(4) A {
int a;
int b;
};
class 与 strcut 采用字节对齐来安排类内的内存,默认以最大的成员字节为 对齐字节数,未满均以对齐字节数的额外差值补齐。
bool:(平台不同影响不同)
建议:
- 作为条件表达式,使用自身作为条件,而非 bool == false 。。。
- bool 不要作为函数参数,因为传递的过程是非常难以理解的,使用 enum class 来替换。
char
在linux平台下是 -128 - 128 (signed char)
在其他平台下 0 - 256 (unsigned char)
constexpr : 常量表达式
可以作为函数返回值,或是其他变量等等(只要给定情况下,能在编译期间求值的话,均不会在运行期间求值)
const_cast
出现 const_cast 的情况即代表代码结构本身有问题(打破了正常的规则,对不变的性质做出改变)
nullptr (c++11)与 NULL和 0 的区别与联系
nullptr 是作为空指针类型进行表示的,类型是(std::nullptr_t),目的是在类型推断时避免 使用(NULL 和 0 推断结果的异样)。
- 0 推断为 int类型
- NULL 是宏定义,在 c 语言是 (void*)0, 在 c++中 是 0LL
或者
auto p1 = 0;
decltype(p1) p2; // p 是int类型
auto p1 = NULL;
decltpe(p1) p2; // p2 是 longlong类型
auto p1 = nullptr;
decltype(p1) p2; // p2 是指针类型:std::nullptr_t
(static_cast) (const_cast ) (reinterpret_cast) (dynamic_cast ) (c like cast) 五种类型转换的区别
四种类型各尽其职务,互相独立,操作之间不会存在相同规则
static_cast : 显式地进行 符合c++程序的 所有隐式转换规则,如 非const 转换为 const, int ->dobule ,可能会损失精度,当然也不能进行 const_cast 操作了,非隐式转换
const_cast : 只能改变运算对象的底层 const, 出现该 类型转换则违背了一定的不进行改变 的规则,需要检查代码是否合理
reinterpret_cast : 它可以无视种族隔离,随便搞。但就像生物的准则,不符合自然规律的随意杂交只会得到不能长久生存的物种。随意在不同类型之间使用reinterpret_cast,也会造成程序的破坏和不能使用。不能进行 const_cast 操作
dynamic_cast :用于将基类的指针或引用安全地转换为派生类的指针或引用。
static_assert (c++11)
编译时发现条件不满足, 输出 msg, 对一些行为的安全处理与警告信息的传递
OOP
前置声明:
- 使用前置声明,能避免在头文件定义时使用其他头文件,额外地对 “两个类之间的先后存在”完美地解释。
- 除非必须引用头文件达到现文件的实现(若在源文件实现,在源文件中引入),否则尽可能少引用,会减缓编译器编译速度
一般来说:
- 当实现一个目的时,需要清楚地明白引入的东西的大小或者里面有的东西,这时务必引入(作为基类,类成员或者标准库模板参数)
- 当作为 返回值,接口等等 使用前置声明即可
三五原则
在原则上一定要考虑好 自赋值与赋值前内存的管理问题
assert 预处理宏
assrt( expr) :我们使用 assert 处理一些行为上必须正确的事情。
对expr求值,如果表达式为假,assert 输出信息并终止程序的执行,如果表达式为真,则assert什么都不做
析构函数不要被默认合成
析构函数默认是 内联且 noexpect,因为内联展开的析构函数是非常庞大的,因此我们要阻止该合成方式。
内部声明,外部定义且非内联。
析构函数不能抛出异常
析构函数默认是 noexcept,抛出异常,会直接导致程序崩溃, std::terminal
如果我们强制 noexcept(false), 会在程序进行中出现很多不必要的问题。
鼓励构造函数抛出异常
构造函数结束后对象一定要是符合要求的,所以抛出异常是对要求的安全处理,避免后来使用造成不必要的问题。
vitrual 虚函数声明
vitrual 声明要权衡,因为 声明为 virtual 编译器就会对其做额外的事情。
智能指针
- auto_ptr
- shared_ptr
- weak_ptr
- unique_ptr
- enable_share_from_this (CRTP)
- 智能指针的区别
auto_ptr 是c++11摒弃的智能指针,当指向空或者离开指定作用域时会销毁其管理的内存,存在拷贝操作,会将其内存转交,但如果使用转交后的智能指针会造成崩溃问题。可以说为了避免潜在的内存问题导致崩溃因此 c++11摒弃。
shared_ptr 是共享指针,拷贝其指针会进行内存共享,每个shared_ptr 都会保存引用计数,随着指针置空或者离开作用域销毁,其引用计数递减,递减为 0 则销毁其内存。
shared_ptr 循环引用导致的问题:
#include <memory>
#include <utility>
namespace mynamespace {
class woman;
class man {
using Ptr = std::shared_ptr<woman>;
public:
man() = default;
~man() {
std::cout << "man is died! "; }
void set_womanPtr(Ptr p) {
womanPtr_ = std::move(p); }
private:
Ptr womanPtr_;
};
class woman {
using Ptr = std::shared_ptr<man>;
public:
woman() = default;
~woman() {
std::cout << "woman is died! "; }
void set_manPtr(Ptr p) {
manPtr_ = std::move(p); }
private:
Ptr manPtr_;
};
}
int main() {
std::shared_ptr<woman> woman_ptr = std::make_shared<woman>();
std::shared_ptr<man> man_ptr = std::make_shared<man>();
if(woman_ptr && man_ptr ) {
woman_ptr->set_manPtr(man_ptr);
man_ptr->set_womanPtr(woman_ptr);
}
//woman_ptr 管理了一个woman动态内存 且其成员管理了 man 动态指针
//man_ptr 管理了一个man 动态指针,且其成员管理了 woman 动态指针
//其shared_ptr 的引用计数均为2
//离开作用域先销毁 man_ptr 其 管理man 的智能指针为1 ,woman 动态内存为 2
//离开作用域再销毁 woman_ptr 其 管理man 的智能指针为1 ,woman 动态内存为 1
}
这样两个类之间彼此互相存放对方的 shared_ptr,当进行相互引用时,就会导致循环引用内存无法释放,两个类对象之间的动态内存均不能被有效处理。
为了打破以上循环引用带来的问题:引入了 weak_ptr
weak_ptr 是一种不控制 所指向对象生存期的智能指针,它指向一个 shared_ptr的对象, 将 weak_ptr 绑定到 shared_ptr 不会改变 shared_ptr 的引用计数
一旦绑定的 shared_ptr 被销毁,对象会被释放,即使有 weak_ptr 指向对象,对象依然会被释放,因此 weak_ptr 的名字抓住了智能指针的 ”弱“ 共享对象的特点.
这样使用weak_ptr 作为 循环引用 的 ptr, 销毁该shared_ptr 就会有效地对对其内存进行释放,不会导致循环引用的问题。
unique_ptr : 独一无二指针,一个 unique_ptr ”拥有“ 它所指的对象,只能有一个 unique_ptr 指向一个给顶对象,当unique_ptr被销毁时,该所指的对象也被销毁。
- 将类自身传递给智能指针参数的解决办法
enable_share_from_this (CRTP): 从该enable_share_from_this<> 模板派生,就可以将其自身传递给智能指针参数。
使用 share_from_this() 从类到指针的转变。
- 智能指针与原生指针的效率与使用智能指针的建议
因为 智能指针是以时间空间效率为代价换取了安全可靠的内存管理,一般来说:
在执行 bool 转换当作条件表达式与解引用两种行为的操作时效率比较为:
- 原生指针 :1.2
- 智能指针:1.49
- weak_ptr : 14 (有 lock 操作,尽量不使用 weak_ptr 来指向 shared_ptr, 代价比较高),weak_ptr 问题很多注意一下。
建议:
- 优先使用类实例,万不得已使用 unique_ptr, 实在不行使用 shared_ptr
- 使用 const 引用传递共享指针
- 多线程方面:
- 其中在初始化智能指针时会进行 两次 new 行为,一次 new 其管理内存,一次 new 自身,因此使用 std::make_shared<>() 融合其new 的过程是时间效率高的。
STL
多线程编程
什么是多线程编程 ?
多线程(multithreading),是指从软件或者硬件上实现多个线程并发