Narlgc's Notebook

小小程序员的杂乱记事本


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

  • 公益404

  • 搜索

cmake学习笔记

发表于 2019-09-28 |

CMake笔记

Background

CMake意为cross-platform make,可用于管理c/c++工程。
CMake解析配置文件CMakeLists.txt生成Makefile,相比直接用Makefile管理工程,CMake更灵活和简单。

最简单的CMakeLists.txt

1
2
PROJECT(HELLO)    //更简单,这一句都可以省略!!!
ADD_EXECUTABLE(hello main.c)

然后执行cmake .,后输入make(单线程编译)或者make -jN(多线程编译:例如make -j4,即使用4个线程编译)

内置变量

变量 说明
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR 三条等价
[projectname]_SOURCE_DIR projectname通过PROJECT(projectName)定义
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR 三条等价
[projectname]_BINARY_DIR projectname通过PROJECT(projectName)定义
CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRRENT_BINARY_DIR 如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致;
如果是 out-ofsource 编译,他指的是 target 编译目录
CMAKE_CURRENT_LIST_FILE 调用这个变量的 CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_LINE 这个变量所在的行
CMAKE_MODULE_PATH 定义自己的 cmake 模块所在的路径
为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
此时可以通过INCLUDE指令来调用自己的模块
EXECUTABLE_OUTPUT_PATH 更改生成的可执行文件路径
LIBRARY_OUTPUT_PATH 更改生成的库文件路径
PROJECT_NAME 返回通过 PROJECT 指令定义的项目名称

系统信息

  • CMAKE_MAJOR_VERSION,CMAKE主版本号,比如2.4.6中的2
  • CMAKE_MINOR_VERSION,CMAKE次版本号,比如2.4.6中的4
  • CMAKE_PATCH_VERSION,CMAKE补丁等级,比如2.4.6 中的6
  • CMAKE_SYSTEM,系统名称,比如Linux-2.6.22
  • CMAKE_SYSTEM_NAME,不包含版本的系统名,比如Linux
  • CMAKE_SYSTEM_VERSION,系统版本,比如2.6.22
  • CMAKE_SYSTEM_PROCESSOR,处理器名称,比如i686.
  • UNIX,在所有的类UNIX平台为TRUE,包括OS X和cygwin
  • WIN32,在所有的win32平台为TRUE,包括cygwin

开关选项

变量 说明
CMAKE_C_FLAGS 设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加
CMAKE_CXX_FLAGS 设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加

注:

1
2
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") 
//g++使用 -fpermissive 参数进行编译,即兼容一些老的语法,但是一些语法错误也会被忽略

命令

注意:

  • 参数使用括弧括起,参数之间使用空格或分号分开。command(avg1 avg2 ...)
  • 指令是大小写无关的,参数和变量是大小写相关的。但推荐全部使用大写指令。
命令 用例 说明
make clean make clean 对构建结果进行清理
list常用 list(APPEND <list> <element> [<element> ...]) 将会在该list之后追加若干元素
list(REMOVE_ITEM SRC_LIST a.cpp) 从SRC_LIST中删除a.cpp
SET set(CODE_DIR ${PROJECT_SOURCE_DIR})
SET(HELLO_SRC main.c)
SET(HELLO_SRC "main.c")
定义变量并赋值,可以接受多个
SET(SRC_LIST main.c t1.c t2.c)
MESSAGE message([SEND_ERROR or STATUS or FATAL_ERROR], “message”) 类似于printf
${} 变量的引用、取值(IF语句中直接变量名取值)
FILE FILE(GLOB_RECURSE SRC_LIST "*.cpp")
$ENV{NAME} MESSAGE(STATUS “HOME dir: $ENV{HOME}”) 调用系统的环境变量
SET(ENV{NAME} VAL) 设置环境变量
ADD_DEFINITIONS ADD_DEFINITIONS(-DENABLE_DEBUG -DABC) 向 C/C++编译器添加 -D 定义(相当于添加了#define)–>添加编译参数
ADD_DEPENDENCIES ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...) 定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建
ADD_SUBDIRECTORY ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除
ENABLE_TESTING ENABLE_TESTING() 控制 Makefile 是否构建 test 目标,一般情况这个指令放在工程的主CMakeLists.txt 中.
ADD_TEST ADD_TEST(testname Exename arg1 arg2 ...) testname 是自定义的 test 名称,Exename 可以是构建的目标文件也可以是外部脚本等等。后面连接传递给可执行文件的参数。如果没有在同一个 CMakeLists.txt 中打开ENABLE_TESTING()指令,任何 ADD_TEST 都是无效的。
AUX_SOURCE_DIRECTORY AUX_SOURCE_DIRECTORY(dir SRC_LIST) 将dir目录下所有源文件列表存储在一个变量SRC_LIST中,被用来自动构建源文件列表
CMAKE_MINIMUM_REQUIRED CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR) 如果 cmake 版本小与 2.5,则出现严重错误,整个过程中止
INCLUDE INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
用来载入 CMakeLists.txt 文件,也用于载入预定义的 cmake 模块.
OPTIONAL 参数的作用是文件不存在也不会产生错误
INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES(/usr/include/hello) 用来向工程添加多个特定的头文件搜索路径==指定头文件的搜索路径,相当于指定gcc的-I参数
LINK_DIRECTORIES LINK_DIRECTORIES(directory1 directory2 ...) 添加非标准的共享库搜索路径
TARGET_LINK_LIBRARIES TARGET_LINK_LIBRARIES(target library1<debug或optimized> library2...) 为 target 添加需要链接的共享库(动态库、静态库)
ADD_LIBRARY ADD_LIBRARY(Hello hello.cxx) 将hello.cxx编译成静态库如libHello.a

ADD_LIBRARY

1
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
类型有三种:
  • SHARED 动态库 后缀为.so –>生成文件lib[libname].so
  • STATIC 静态库 后缀为.a –>生成文件lib[libname].a
  • MODULE 在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待

EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。

1
2
3
4
5
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) //动态库
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC}) //静态库
ADD_EXECUTABLE(demo main.c)
TARGET_LINK_LIBRARIES(demo hello) //hello为一个库

INCLUDE_DIRECTORIES

1
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面。你可以通过两种方式来进行控制搜索路径添加的方式:

  1. CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以将添加的头文件搜索路径放在已有路径的前面。(另一个变量CMAKE_INCLUDE_DIRECTORIES_BEFORE也可使用!!)
  2. 通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。

ADD_DEFINITIONS

1
2
3
add_definitions(
-D__SHORT_FILE__=__FILE__
)

等价于

1
gcc -D__SHORT_FILE__=__FILE__ main.c -o demo

或者

1
2
cmake -D__SHORT__FILE__=__FILE__ main.c -o demo
cmake -DMY_VAR=hello .

或者在main.c头部添加了
#define SHORT_FILE FILE

注: ADD_DEFINITIONS本是添加编译参数,如:

add_definitions(-DDEBUG) 将在gcc命令行添加DEBUG宏定义;
add_definitions( “-Wall -ansi –pedantic –g”) 添加多个编译参数

ENABLE_TESTING & ADD_TEST

1
2
3
ENABLE_TESTING()
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/BIN/main 2 10)
SET_TESTS_PROPERTIES(mytest PROPERTIES PASS_REGULAR_EXPRESSION "1024") \\用来测试输出中是否包含后面的字符串

AUX_SOURCE_DIRECTORY

1
2
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})

FILE指令

1
2
3
4
5
6
7
8
9
10
11
FILE(WRITE filename "message towrite"... )
FILE(APPEND filename "message towrite"... )
FILE(READ filename variable)
FILE(GLOB variable [RELATIVE path][globbing expressions]...)
FILE(GLOB_RECURSE variable [RELATIVE path] [globbingexpressions]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)

注: FILE(GLOB_RECURSE SRC_LIST "*.cpp")即将该目录下及所有子文件夹内的++所有++后缀为.cpp的文件的路径,全部放入SRC_LIST变量中。

1
2
3
4
5
6
查找当前目录下所有的源文件并保存到SRC_LIST变量里
aux_source_directory(. SRC_LIST)
查找src目录下所有以cmake开头的文件并保存到CMAKE_FILES变量里
file(GLOB CMAKE_FILES "src/cmake*")
file命令同时支持目录递归查找
file(GLOB_RECURSE CMAKE_FILES "src/cmake*")

IF

1
2
3
4
5
6
7
8
9
10
IF(expression)
# THEN section.
COMMAND1(ARGS ...)COMMAND2(ARGS ...)
...
ELSE(expression)
# ELSE section.
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF(expression)

表达式的使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或
<var>_NOTFOUND 时,表达式为真。
IF(NOT var ),与上述条件相反。
IF(var1 AND var2),当两个变量都为真是为真。
IF(var1 OR var2),当两个变量其中一个为真时为真。
IF(COMMAND cmd),当给定的 cmd 确实是命令并可以调用是为真。
IF(EXISTS dir)或者 IF(EXISTS file),当目录名或者文件名存在时为真。
IF(file1 IS_NEWER_THAN file2),当 file1 比 file2 新,或者 file1/file2 其
中有一个不存在时为真,文件名请使用完整路径。
IF(IS_DIRECTORY dirname),当 dirname 是目录时,为真。
IF(variable MATCHES regex)
IF(string MATCHES regex)

当给定的变量或者字符串能够匹配正则表达式 regex 时为真。比如:

1
2
3
4
5
6
7
8
9
IF("hello" MATCHES "ell")
MESSAGE("true")
ENDIF("hello" MATCHES "ell")IF(variable LESS number)

IF(string LESS number)
IF(variable GREATER number)
IF(string GREATER number)
IF(variable EQUAL number)
IF(string EQUAL number)

数字比较表达式

1
2
3
4
5
6
IF(variable STRLESS string)
IF(string STRLESS string)
IF(variable STRGREATER string)
IF(string STRGREATER string)
IF(variable STREQUAL string)
IF(string STREQUAL string)

按照字母序的排列进行比较.

1
IF(DEFINED variable),如果变量被定义,为真。

一个小例子,用来判断平台差异:

1
2
3
4
5
6
7
IF(WIN32)
MESSAGE(STATUS “This is windows.”)
#作一些 Windows 相关的操作
ELSE(WIN32) //容易引起歧义
MESSAGE(STATUS “This is not windows”)
#作一些非 Windows 相关的操作
ENDIF(WIN32)

或者配合SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)使用后,上述语句可简化为如下形式:

1
2
3
IF(WIN32)
ELSE()
ENDIF()

如果配合 ELSEIF 使用,可能的写法是这样:

1
2
3
4
5
6
7
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF(WIN32)

WHILE

1
2
3
4
5
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)

FOREACH

  1. 列表

    1
    2
    3
    4
    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    FOREACH(F ${SRC_LIST})
    MESSAGE(${F})
    ENDFOREACH(F)
  2. 范围

    1
    2
    3
    FOREACH(VAR RANGE 10)  //从0到10以1步进
    MESSAGE(${VAR})
    ENDFOREACH(VAR)
  3. 范围和步进

    1
    2
    3
    FOREACH(A RANGE 5 15 3)  //从5开始到15结束,以3为步进
    MESSAGE(${A})
    ENDFOREACH(A)

注:直到遇到ENDFOREACH指令,整个语句块才会得到真正的执行。

内部构建 vs 外部构建

构建分为内部构建(in-sourcebuild)和外部构建(out-of-source build)。
外部构建一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录。而cmake强烈推荐的是外部构建。

|——build
|——CMakeLists.txt
|——CMakeLists.txt.bak
|____main.c

根目录下包含源文件.c、CMakeList.txt。然后创建*build**文件夹,并cd build下,再执行cmake ..,再make -j4.

注: 通过外部编译进行工程构建,HELLO_SOURCE_DIR仍然指代工程路径,即/backup/cmake/t1;而 HELLO_BINARY_DIR 则指代编译路径,即/backup/cmake/t1/build.

FAQ

1) 怎样获得一个目录下的所有源文件

>>  aux_source_directory(<dir> <variable>)
>>  将dir中所有源文件(不包括头文件)保存到变量variable中,然后可以add_executable (ss7gw ${variable})这样使用。

2) 怎样指定项目编译目标
>> project命令指定

3) 怎样添加动态库和静态库
>> target_link_libraries命令添加即可

4) 怎样在执行CMAKE时打印消息
>> message([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
>> 注意大小写

5) 怎样指定头文件与库文件路径
>> include_directories与link_directories
>>可以多次调用以设置多个路径
>> link_directories仅对其后面的targets起作用

6) 怎样区分debug、release版本
>>建立debug/release两目录,分别在其中执行cmake -DCMAKE_BUILD_TYPE=Debug(或Release),需要编译不同版本时进入不同目录执行make即可;
Debug版会使用参数-g;Release版使用-O3 –DNDEBUG
>> 另一种设置方法——例如DEBUG版设置编译参数DDEBUG

IF(DEBUG_mode)
    add_definitions(-DDEBUG)
ENDIF()
在执行cmake时增加参数即可,例如cmake -D DEBUG_mode=ON

7) 怎样设置条件编译
例如debug版设置编译选项DEBUG,并且更改不应改变CMakelist.txt

>> 使用option command,eg:

option(DEBUG_mode "ON for debug or OFF for release" ON)
IF(DEBUG_mode)
    add_definitions(-DDEBUG)
ENDIF()

>> 使其生效的方法:首先cmake生成makefile,然后make edit_cache编辑编译选项;Linux下会打开一个文本框,可以更改,该完后再make生成目标文件——emacs不支持make edit_cache;
>> 局限:这种方法不能直接设置生成的makefile,而是必须使用命令在make前设置参数;对于debug、release版本,相当于需要两个目录,分别先cmake一次,然后分别make edit_cache一次;
>> 期望的效果:在执行cmake时直接通过参数指定一个开关项,生成相应的makefile——可以这样做,例如cmake –DDEBUGVERSION=ON

8) 怎样添加编译宏定义
>> 使用add_definitions命令,见命令部分说明

9) 怎样添加编译依赖项
用于确保编译目标项目前依赖项必须先构建好
>>add_dependencies

10) 怎样指定目标文件目录
>> 建立一个新的目录,在该目录中执行cmake生成Makefile文件,这样编译结果会保存在该目录——类似
>> SET_TARGET_PROPERTIES(ss7gw PROPERTIES
RUNTIME_OUTPUT_DIRECTORY “${BIN_DIR}”)

11) 很多文件夹,难道需要把每个文件夹编译成一个库文件?
>> 可以不在子目录中使用CMakeList.txt,直接在上层目录中指定子目录

12) 怎样设定依赖的cmake版本
>> cmake_minimum_required(VERSION 2.6)

13) 相对路径怎么指定
>> ${projectname_SOURCE_DIR}表示根源文件目录,${ projectname _BINARY_DIR}表示根二进制文件目录?

14) 怎样设置编译中间文件的目录
>> TBD

15) 怎样在IF语句中使用字串或数字比较
>>数字比较LESS、GREATER、EQUAL,字串比STRLESS、STRGREATER、STREQUAL,
>> Eg:

set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
set(AAA abc)
IF(AAA STREQUAL abc)
    message(STATUS "true")   #应该打印true
ENDIF()

16) 更改h文件时是否只编译必须的cpp文件
>> 是

17) 机器上安装了VC7和VC8,CMAKE会自动搜索编译器,但是怎样指定某个版本?
>> TBD

18) 怎样根据OS指定编译选项
>> IF( APPLE ); IF( UNIX ); IF( WIN32 )

19) 能否自动执行某些编译前、后命令?
>> 可以,TBD

20) 怎样打印make的输出
>> make VERBOSE=1 #看到 make 构建的详细过程


还未吃透!!!

###包含目录和链接目录
将./include和./abc加入包含目录列表

include_directories(
    ./include
    ./abc
)

将./lib加入编译器链接阶段的搜索目录列表

link_directories(
    ./lib
)

添加生成目标

使用SRC_LIST源文件列表里的文件生成一个可执行文件hello

add_executable(hello ${SRC_LIST})

使用SRC_LIST源文件列表里的文件生成一个静态链接库libhello.a

add_library(hello STATIC ${SRC_LIST})

使用SRC_LIST源文件列表里的文件生成一个动态链接库libhello.so

add_library(hello SHARED ${SRC_LIST})

将若干库文件链接到生成的目标hello(libhello.a或libhello.so)

target_link_libraries(hello A B.a C.so)

需要注意的是,target_link_libraries里库文件的顺序符合gcc链接顺序的规则,即被依赖的库放在依赖它的库的后面,比如上面的命令里,libA.so可能依赖于libB.a和libC.so,如果顺序有错,链接时会报错。还有一点,B.a会告诉CMake优先使用静态链接库libB.a,C.so会告诉CMake优先使用动态链接库libC.so,也可直接使用库文件的相对路径或绝对路径。使用绝对路径的好处在于,当依赖的库被更新时,make的时候也会重新链接。

C++ 服务器编程开发

发表于 2019-09-04 | 分类于 Coding |

Day 01

效率

开发效率:
C++11 -> auto、lambda、move
同步、异步、多线程、多进程…
运行效率

预备知识

  1. TCP/IP (网络编程都是此种协议)
    • TCP -> 传输控制协议
    • IP -> 网际协议
  2. Socket
  3. C++
    • 指针、引用、地址、循环、函数、类…
    • std::shared_ptr、std::thread、std::aync、decltype、std::future、right、reference
  4. 第三方库
    • BOOST

问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 反转链表
#include<algorithm>

using namespace std;
struct Node{
int* next;
int value;
}

void pirntNodeList(Node* n){
while(n)
{
cout << n->value << " ";
n = n->next;
}
cout << "\n";
}

Node* reverseList(Node* node)
{

return 0;
}

void quizOne()
{
Node* n = 0;
for(int i = 0; i<3; i++)
{
n->next = Node(i);
}
}

IP网际协议详解

IP = Internet protocol suite

OSI模型(Open System Interconnection model)


注意: 七个层级,过于复杂。

IP模型

数据进入协议栈的封装


注意: Frame data 长度有要求,最小46字节,不足添0;最大1500个字节。

MTU(最大传输单元),当传输数据 > MTU时,则在IP层需要拆分成小包。

IP特点

  • 不可靠(unreliable)
  • 无连接(connectionless)

IP数据报格式首部字段


注意:【小小】 网络数据都是按照Big Endian【大端存储】传送的;家用操作系统都是Littele Endian数据存储。
Big_Small_Endian.jpg
思考题:

1
2
3
static bool isLittleEndianSystem(){

}

TCP详解

TCP如何利用IP

  • IP的特点
  • TCP将应用程序的传输数据分割成合适的数据块
  • 定时器
  • 延迟确认
  • 检验和CRC
  • 流量控制

TCP首部


注意: IP首部20Byte,TCP首部20Byte,如需在网络传输,还需填充6Byte。

所谓的Socket , 即ip+port

ip 来自 IP首部
port 来自 TCP首部

  • Sequence Number 用于标记定时器对那一数据包计时。
  • Acknowlegment Number 与延迟确认有关 -> if ACK set
  • Checksum 与 检验和 有关
  • Data offset – 数据长度
  • URG -> 数据紧急标志位
  • ACK -> 数据确认标识位
  • PSH -> push
  • RST -> reset
  • SYN -> 建立时需要
  • FIN -> 结束

TCP状态

TPC状态变迁

蓝线–server;棕线–client;虚线–异常

TCP的连接(三次握手)

TCP连接的断开(四次握手)

1.为什么断开要四次握手?

全双工,两边都需要确认
主动断开方,会进入TIME_WAIT状态

TCP数据相互传送

  • 交互式(小数据)与成块的数据两种
  • 时间延迟确认
  • Nagle算法[小数据积攒后再发送](游戏开发一般关闭这个算法)
  • 接收窗口大小

TCP内部使用的定时器

  • 重传定时器
  • 坚持定时器(Persist)
  • 保活定时器(KeepAlive)<建议不开启>
  • 2MSL定时器(TIME_WAIT)

实战

Wireshark使用

需安装WinCap方能使用

筛选器

ip.addr==115.239.210.27 && ip.port==80

Socket API (Berkeley sockets, BSD Socket)

头文件

API函数


服务器和客户端的例子

TCP Socket 基本流程图

Code:

Server_main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(int argc, char** argv)
{
char hello[] = "hello world";
struct sockaddr_in sa;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if(-1 == SocketFD)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
}

memset(&sa, 0, sizeof sa);

sa.sin_family = AF_INET;
sa.sin_port = htons(2222);
sa.sin_addr.s_addr = htonl(INADDR_ANY);

if(-1 == bind(SocketFD, (struct sockaddr*)&sa, sizeof sa))
{
perror("bind failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

if(-1 == listen(SocketFD, 10))
{
perror("listen failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

for(;;)
{
int ConnectFD = accept(SocketFD, NULL, NULL);

if(0 > ConnectFD)
{
perror("accept failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

int writeSize = 0;
size_t totalWrite = 0;
while(totalWrite < sizeof(hello))
{
writeSize =
write(ConnectFD, hello + totalWrite, sizeof(hello) - totalWrite);
if(-1 == writeSize)
{
perror("write failed");
close(ConnectFD);
close(SocketFD);
exit(EXIT_FAILURE);
}
totalWrite += writeSize;
}

if(-1 == shutdown(ConnectFD, SHUT_RDWR))
{
perror("shutdown failed");
close(ConnectFD);
close(SocketFD);
exit(EXIT_FAILURE);
}
close(ConnectFD);
}
close(SocketFD);
return EXIT_SUCCESS;
}

Client_main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(int argc, char** argv)
{
struct sockaddr_in sa;
int res;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if(-1 == SocketFD)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
}

memset(&sa, 0, sizeof sa);

sa.sin_family = AF_INET;
sa.sin_port = htons(2222);
res = inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);

if(-1 == connect(SocketFD, (struct sockaddr*)&sa, sizeof sa))
{
perror("connect failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

char buffer[512];
int totalRead = 0;
for(;;)
{
int readSize = 0;
readSize = read(SocketFD, buffer + totalRead, sizeof(buffer)-totalRead);
if(readSize == 0)
{
break;
}
else if(readSize == -1)
{
perror("read failed");
close(SocketFD);
exit(EXIT_FAILURE);
}
totalRead += readSize;
}
buffer[totalRead] = 0;
printf("get from server: %s\n",buffer);

(void)shutdown(SocketFD, SHUT_RDWR);

close(SocketFD);
return EXIT_SUCCESS;
}

SQL常用命令汇总

发表于 2019-09-03 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
USE day15;

-- 创建表
CREATE TABLE teacher(
id INT,
NAME VARCHAR(20)
)
-- 查看所有表
SHOW TABLES;

DESC student;

DROP TABLE student;

CREATE TABLE student(
id INT,
NAME VARCHAR(20),
gender VARCHAR(2),
age INT
)

-- ********一、增删改数据********* ---
-- 1.1 增加数据
-- 插入所有字段。一定依次按顺序插入
INSERT INTO student VALUES(1,'张三','男',20);
-- 注意不能少或多字段值
-- INSERT INTO student VALUES(2,'李四','女');
-- 插入部分字段
INSERT INTO student(id,NAME) VALUES(2,'李四');

-- 1.2 修改数据
-- 修改所有数据(建议少用)
UPDATE student SET gender='女';
-- 带条件的修改(推荐使用)
UPDATE student SET gender='男' WHERE id=1; -- 修改id为1的学生,修改性别为男
-- 修改多个字段,注意: SET 字段名=值,字段名=值,....
UPDATE student SET gender='男',age=30 WHERE id=2;

-- 1.3 删除数据
-- 删除所有数据(建议少用)
DELETE FROM student;
-- 带条件的删除(推荐使用)
DELETE FROM student WHERE id=2;
-- 另一种方式
-- delete from: 可以全表删除 1)可以带条件删除 2)只能删除表的数据,不能删除表的约束 3)使用delete from删除的数据可以回滚(事务)
-- truncate table: 可以全表删除 1)不能带条件删除 2)即可以删除表的数据,也可以删除表的约束 3)使用truncate table删除的数据不能回滚
TRUNCATE TABLE student;


CREATE TABLE test(
id INT PRIMARY KEY AUTO_INCREMENT, -- 自增长约束
NAME VARCHAR(20)
)
DESC test;

-- 1.
DELETE FROM test;
-- 2
TRUNCATE TABLE test;

INSERT INTO test(NAME) VALUES('张三');
INSERT INTO test(NAME) VALUES('张三2');
INSERT INTO test(NAME) VALUES('张三3');

SELECT * FROM test;

-- truncate table student where id=2; 不能带条件

-- 查询数据
SELECT * FROM student;

-- ********二、查询数据(select)*******--
-- 2.1 查询所有列
SELECT * FROM student;

-- 2.2 查询指定列
SELECT id,NAME,gender FROM student;

-- 2.3 查询时指定别名(as)
-- 注意: 在多表查询是经常使用表的别名
SELECT id AS '编号',NAME AS '姓名' FROM student;

-- 2.4 查询时添加常量列
-- 需求: 在查询student表时添加一个班级列,内容为“java就业班”
SELECT id,NAME,gender,age,'java就业班' AS '年级' FROM student;

-- 2.5 查询时合并列
-- 需求: 查询每个学生的servlet和jsp的总成绩
SELECT id,NAME,(servlet+jsp) AS '总成绩' FROM student;
-- 注意:合并列只能合并数值类型的字段
SELECT id,(NAME+servlet) FROM student;

-- 2.6 查询时去除重复记录(DISTINCT)
-- 需求: 查询学生的性别 男 女
SELECT DISTINCT gender FROM student;
-- 另一种语法
SELECT DISTINCT(gender) FROM student;
-- 需求: 查询学生所在的地区
SELECT DISTINCT address FROM student;

-- 2.7 条件查询(where)
-- 2.7.1 逻辑条件: and(与) or(或)
-- 需求: 查询id为2,且姓名为李四的学生
SELECT * FROM student WHERE id=2 AND NAME='李四'; -- 交集

-- 需求: 查询id为2,或姓名为张三的学生
SELECT * FROM student WHERE id=2 OR NAME='张三'; -- 并集

-- 2.7.2 比较条件: > < >= <= = <>(不等于) between and (等价于>= 且 <=)
-- 需求: 查询servlet成绩大于70分的学生
SELECT * FROM student WHERE servlet>70;

-- 需求: 查询jsp成绩大于等于75,且小于等于90分的学生
SELECT * FROM student WHERE jsp>=75 AND jsp<=90;
-- 另一个语法
SELECT * FROM student WHERE jsp BETWEEN 75 AND 90; -- (包前包后)

SELECT * FROM student WHERE gender<>'男';


-- 2.7.3 判空条件(null 空字符串): is null / is not null / ='' / <>''
-- 需求: 查询地址为空的学生(包括null和空字符串)
-- null vs 空字符串
-- null:表示没有值
-- 空字符串:有值的!
-- 判断null
SELECT * FROM student WHERE address IS NULL ;
-- 判断空字符串
SELECT * FROM student WHERE address='';

SELECT * FROM student WHERE address IS NULL OR address=''; -- (包括null和空字符串)

-- 需求: 查询有地址的学生(不包括null和空字符串)
SELECT * FROM student WHERE address IS NOT NULL AND address<>'';


-- 2.7.4 模糊条件: like
-- 通常使用以下替换标记:
-- % : 表示任意个字符
-- _ : 表示一个字符
-- 需求: 查询姓‘张’的学生
SELECT * FROM student WHERE NAME LIKE '李%';

-- 需求: 查询姓‘李’,且姓名只有两个字的学生
SELECT * FROM student WHERE NAME LIKE '李_';


--- 练习 --
CREATE TABLE student2(
id INT,
NAME VARCHAR(20),
chinese FLOAT,
english FLOAT,
math FLOAT
);

INSERT INTO student2(id,NAME,chinese,english,math) VALUES(1,'张小明',89,78,90);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(2,'李进',67,53,95);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(3,'王五',87,78,77);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(4,'李一',88,98,92);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(5,'李来财',82,84,67);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(6,'张进宝',55,85,45);
INSERT INTO student2(id,NAME,chinese,english,math) VALUES(7,'黄蓉',75,65,30);



-- 查询表中所有学生的信息。
SELECT * FROM student2;

-- 查询表中所有学生的姓名和对应的英语成绩。
SELECT NAME,english FROM student2;

-- 过滤表中英语成绩的重复数据
SELECT DISTINCT(english) FROM student2;

-- 使用别名表示学生分数。
SELECT NAME AS '姓名',chinese AS '语文',english AS '英语',math AS '数学' FROM student2;

-- 查询姓名为李一的学生成绩
SELECT * FROM student2 WHERE NAME='李一';

-- 查询英语成绩大于等于90分的同学
SELECT * FROM student2 WHERE english>=90;

-- 查询总分大于200分的所有同学
SELECT * FROM student2 WHERE (chinese+english+math)>200;

-- 查询所有姓李的学生英语成绩。
SELECT NAME,english FROM student2 WHERE NAME LIKE '李%';

-- 查询英语>80或者总分>200的同学
SELECT * FROM student2 WHERE english>80 OR (chinese+english+math)>200

-- 统计每个学生的总分。
SELECT id,NAME,(chinese+english+math) AS '总成绩' FROM student2;

-- 在所有学生总分数上加10分特长分。
SELECT id,NAME,(chinese+english+math+10) AS '总成绩' FROM student2;

SELECT * FROM student;


-- 2.8 聚合查询(使用聚合函数的查询)
-- 常用的聚合函数: sum() avg() max() min() count()
-- 需求:查询学生的servlet的总成绩 (sum() :求和函数)
SELECT SUM(servlet) AS 'servlet的总成绩' FROM student;

-- 需求: 查询学生的servlet的平均分
SELECT AVG(servlet) AS 'servlet的平均分' FROM student;

-- 需求: 查询当前servlet最高分
SELECT MAX(servlet) AS '最高分' FROM student;

-- 需求: 查询最低分
SELECT MIN(servlet) AS '最低分' FROM student;

-- 需求: 统计当前有多少学生(count(字段))
SELECT COUNT(*) FROM student;

SELECT COUNT(id) FROM student;

-- 注意:count()函数统计的数量不包含null的数据
-- 使用count统计表的记录数,要使用不包含null值的字段
SELECT COUNT(age) FROM student;


SELECT * FROM student;
-- 2.9 分页查询(limit 起始行,查询几行)
-- 起始行从0开始
-- 分页:当前页 每页显示多少条
-- 分页查询当前页的数据的sql: SELECT * FROM student LIMIT (当前页-1)*每页显示多少条,每页显示多少条;

-- 需求: 查询第1,2条记录(第1页的数据)
SELECT * FROM student LIMIT 0,2;
-- 查询第3,4条记录(第2页的数据)
SELECT * FROM student LIMIT 2,2;
-- 查询第5,6条记录(第3页的数据)
SELECT * FROM student LIMIT 4,2;
-- 查询第7,8条记录 (没有记录不显示)
SELECT * FROM student LIMIT 6,2;

-- 2.10 查询排序(order by )
-- 语法 :order by 字段 asc/desc
-- asc: 顺序,正序。数值:递增,字母:自然顺序(a-z)
-- desc: 倒序,反序。数值:递减,字母:自然反序(z-a)

-- 默认情况下,按照插入记录顺序排序
SELECT * FROM student;

-- 需求: 按照id顺序排序
SELECT * FROM student ORDER BY id ASC;
SELECT * FROM student ORDER BY id; -- 默认正序

SELECT * FROM student ORDER BY id DESC;-- 反序

-- 注意:多个排序条件
-- 需求: 按照servlet正序,按照jsp的倒序
SELECT * FROM student ORDER BY servlet ASC,jsp DESC;

-- 2.11 分组查询(group by)
-- 需求: 查询男女的人数
-- 预期结果:
-- 男 3
--- 女 2
-- 1) 把学生按照性别分组(GROUP BY gender)
-- 2) 统计每组的人数(COUNT(*))
SELECT gender,COUNT(*) FROM student GROUP BY gender;

-- 2.12 分组查询后筛选
-- 需求: 查询总人数大于2的性别
-- 1) 查询男女的人数
-- 2)筛选出人数大于2的记录(having)
--- 注意: 分组之前条件使用where关键字,分组之前条件使用having关键字
SELECT gender,COUNT(*) FROM student WHERE GROUP BY gender HAVING COUNT(*)>2;



-- 给student表添加servlet和jsp成绩列
ALTER TABLE student ADD servlet INT,ADD jsp INT;
ALTER TABLE student ADD servlet INT;
ALTER TABLE student ADD address VARCHAR(10);
DESC student;
UPDATE student SET servlet=70,jsp=85 WHERE id=1;
UPDATE student SET servlet=65,jsp=90 WHERE id=2;
UPDATE student SET gender='女' WHERE id=2;
UPDATE student SET address='广州天河' WHERE id=1;
UPDATE student SET address='广州天河' WHERE id=2;
UPDATE student SET address='广州番禺' WHERE id=3;

INSERT INTO student VALUES(4,'陈六','男',28,75,80,'');
INSERT INTO student VALUES(5,'李七','男',30,64,83,NULL);
INSERT INTO student VALUES(6,'李八八','男',35,67,82,'广州天河');

使用hexo+github搭建博客 笔记

发表于 2019-09-01 | 分类于 Coding |

常用命令汇总

命令 简写 作用
hexo deploy hexo d 部署到github等
hexo server hexo s 开启服务器,
–debug 开启调试模式
hexo clean 清楚缓存文件及静态文件
hexo generate hexo g 生成静态网页,
-f 等同于hexo clean && hexo g;
-d 等同于hexo g && hexo d
hexo new “new file title” 创建新文章,若题目中无空格,可省去双引号
hexo new page tags 在source创建tags文件夹,且生成index.md的文章

注意:可以做个命令统计脚本,根据使用频次排序上述命令。

本地资料备份

已在github添加hexo分支,本地资料push到hexo分支,hexo部署由hexo d自动推送到master分支。

1
git push origin hexo

安装 hexo

1
2
3
4
5
npm install hexo-cli -g
hexo init blog
cd blog
npm install
hexo server

更换NexT 主题

1
2
cd blog
git clone https://github.com/iissnan/hexo-theme-next themes/next

修改配置文件
打开_config.yml:
修改主题一行为:

1
theme: next

写文章

1
hexo new [layout] <title>

默认layout使用 _config.yml 中的 default_layout 参数
若标题中包含空格,请使用引号括起来。

1
hexo new page --path about/me "About me"  # 自定义新文章路径

举例

1
hexo new first.md

编辑first.md文件即可

本地启动服务器

1
2
3
hexo server
# 可简写为
hexo s

默认网址:![http://localhost:4000/]
-p 重设端口号
-s 只使用静态文件

部署到github

  1. 安装 github部署工具

    1
    npm install hexo-deployer-git --save
  2. 修改_config.yml

    1
    2
    3
    deploy:
    type: git
    repo: https://github.com/Narglc/Narglc.github.io
  3. 部署到github

    1
    2
    3
    hexo deploy
    # 可简写为:
    hexo d

generate vs deploy

1
2
3
4
hexo generate		# 生成静态文件
-d,--deploy # 文件生成后立即部署网站
# 可简写为:
hexo g

hexo g 仅仅生成静态文件,用于本地部署;hexo d 可推送到github部署

clean

1
hexo clean			#清除缓存文件 (db.json) 和已生成的静态文件 (public)。

代码高亮样式

1
2
# normal | night | night eighties | night blue | night bright  # 5种可用
highlight_theme: night

添加阅读全文按钮 [暂未开启]

只显示文章一部分,多余的需要点击阅读全文来查看,需要在文章中添加

1
<!--more-->

设置网站缩略图标

修改站点配置文件_config.yml中small、medium、apple_touch_icon对应图片地址:

1
2
3
4
5
favicon:
small: /images/favicon-16x16-next.png
medium: /images/favicon-32x32-next.png
apple_touch_icon: /images/apple-touch-icon-next.png
safari_pinned_tab: /images/logo.svg

本地搜索功能

安装 hexo-generator-searchdb 插件

1
npm install hexo-generator-searchdb --save

修改next主题配置文件_config.yml:

1
2
local_search:
enable: true

站点配置文件_config.yml的Extensions后添加:

1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000

侧边栏社交链接

修改next主题配置文件_config.yml:

1
2
3
4
5
6
7
8
9
social:
E-Mail: mailto:qingcheliuzhi@qq.com || envelope
GitHub: https://github.com/Narglc || github
豆瓣: https://www.douban.com/people/Narglc/ || douban
social_icons:
enable: true
icons_only: false
transition: false
微博: weibo # 可设置上述对应的图标

注意: || 前面是链接地址,后面是FontAwsone字符库的图标名

打赏

修改站点配置文件_config.yml:

1
2
3
4
# Reward
reward_comment: Donate comment here
wechatpay: /images/wechatpay.jpg
alipay: /images/alipay.jpg

文章末尾添加版权信息

实现

手动修改主题目录下的 layout/_macro/post.swig 文件,找到 post-footer 标签,添加以下内容div内代码段:

即,将div内代码段插入footer标签开始处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<footer class="post-footer">
<div>
{# 此处判断是否在索引列表中 #}
{% if not is_index %}
<ul class="post-copyright">
<li class="post-copyright-author">
<strong>本文作者:</strong>{{ theme.author }}
</li>
<li class="post-copyright-link">
<strong>本文链接:</strong>
<a href="{{ url_for(page.path) }}" title="{{ page.title }}">{{ page.path }}</a>
</li>
<li class="post-copyright-license">
<strong>版权: </strong>
本站文章均采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/cn/" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0 CN</a> 许可协议,请勿用于商业,转载注明出处!
</li>
</ul>
{% endif %}
</div>
[footer 原有内容]
</footer>

添加显示格式

修改主题目录下的 source/css/_custom/custom.styl 文件:

1
2
3
4
5
6
7
.post-copyright {
margin: 1em 0 0;
padding: 0.5em 1em;
border-left: 3px solid #ff1700;
background-color: #f9f9f9;
list-style: none;
}

站点访问计数

使用不蒜子脚本实现计数

新实现

Next 主题在版本6.0以上已经内置了不蒜子访客统计的代码,修改主题配置文件_config.yml即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
busuanzi_count:
# count values only if the other configs are false
enable: true
# custom uv span for the whole site
site_uv: true
site_uv_header: <i class="fa fa-user"></i> 访问人数
site_uv_footer: 人
# custom pv span for the whole site
site_pv: true
site_pv_header: <i class="fa fa-eye"></i> 总访问量
site_pv_footer: 次
# custom pv span for one page only
page_pv: true
page_pv_header: <i class="fa fa-file-o"></i> 阅读数
page_pv_footer:

Hello World

发表于 2019-08-30 |

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

文本居中的引用

1
2
3
4
5
6
7
8
9
<!-- HTML方式: 直接在 Markdown 文件中编写 HTML 来调用 -->
<!-- 其中 class="blockquote-center" 是必须的 -->
<blockquote class="blockquote-center">blah blah blah</blockquote>

<!-- 标签 方式,要求版本在0.4.5或以上 -->
{% centerquote %}blah blah blah{% endcenterquote %}

<!-- 标签别名 -->
{% cq %} blah blah blah {% endcq %}

效果如下:

blah blah blah

blah blah blah

blah blah blah

Narglc Yu

Narglc Yu

七月的风,八月的雨,一切都已过去。

5 日志
1 分类
5 标签
E-Mail GitHub 豆瓣
© 2019 Narglc Yu
访问人数 人 总访问量 次