TCP 协议

TCP 建立连接 TCP 数据传送 TCP 断开连接


socket 套接字

socket 起源于 Unix,而 Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开 open –> 读写 write/read –> 关闭 close”模式来操作。Socket 就是该模式的一个实现, socket 即是一种特殊的文件,一些 socket 函数就是对其进行的操作(读/写 IO、打开、关闭).说白了 Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。
注意:其实 socket 也没有层的概念,它只是一个 facade 设计模式的应用,让编程变的更简单。是一个软件抽象层。在网络编程中,我们大量用的都是通过 socket 实现的。


socket()函数

1
int  socket(int protofamily, int type, int protocol);
  • protofamily:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称 AF_UNIX,Unix 域 socket)、AF_ROUTE 等等。协议族决定了 socket 的地址类型,在通信中必须采用对应的地址,如 AF_INET 决定了要用 ipv4 地址(32 位的)与端口号(16 位的)的组合、AF_UNIX 决定了要用一个绝对路径名作为地址。
  • type:指定 socket 类型。常用的 socket 类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET 等等(socket 的类型有哪些?)。
  • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC 等,它们分别对应 TCP 传输协议、UDP 传输协议、STCP 传输协议、TIPC 传输协议。

    当`protocol`为`0`时,会自动选择`type`类型对应的默认协议

bind()函数

1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:即 socket 描述字,它是通过 socket()函数创建了,唯一标识一个 socket。bind()函数就是将给这个描述字绑定一个名字。
  • addr:一个 const struct sockaddr *指针,指向要绑定给 sockfd 的协议地址。这个地址结构根据地址创建 socket 时的地址协议族的不同而不同。
  • addrlen:对应的是地址的长度。

close()函数:关闭打开的文件

1
int close(int fd);

recvfrom()/sendto()函数:网络 I/O 操作

1
2
3
4
5
int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
//从指定地址接收UDP数据报

int sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);
//把UDP数据报发给指定地址
\s
socket 描述符。
\buf UDP 数据报缓存地址。
\len UDP 数据报长度。
\flags 该参数一般为 0。
\to sendto()函数参数,struct sockaddr_in 类型,指明 UDP 数据发往哪里报。
\tolen 对方地址长度,一般为:sizeof(struct sockaddr_in)。
\from recvfrom()函数参数,struct sockaddr 类型,指明 UDP 数据从哪里收。
\fromlen recvfrom()函数参数,struct sockaddr_in 类型,指明从哪里接收 UDP 数据报。

inet_pton()函数

1
2
int inet_pton(int af, const char *src, void *dst);
//转换字符串到网络地址
  • 第一个参数 af 是地址族,转换后存在 dst 中
  • af = AF_INET:src 为指向字符型的地址,即 ASCII 的地址的首地址(ddd.ddd.ddd.ddd 格式的),函数将该地址转换为 in_addr 的结构体,并复制在*dst 中.

htons()函数:将端口号由主机字节序转换为网络字节序的整数值
inet_addr()函数:将一个 IP 字符串转化为一个网络字节序的整数值



libcurl库进行http通讯

1. 简介

   libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权,HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传,代理,cookies,和用户认证。
   在基于LibCurl的程序里,主要采用callback function (回调函数)的形式完成传输任务,用户在启动传输前设置好各类参数和回调函数,当满足条件时libcurl将调用用户的回调函数实现特定功能。
   libcurl主要提供了两种发送http请求的方式,分别是Easy interface方式和multi interface方式,前者是采用阻塞的方式发送单条数据,后者采用组合的方式可以一次性发送多条数据.

2. 流程

  1. 在主线程中调用curl_global_init(CURL_GLOBAL_ALL)初始化
  2. 调用curl_easy_init获取一个句柄;
  3. 调用curl_easy_setopt函数设置此次传输的一些基本参数,如 url 地址、http 头、cookie 信息、发送超时时间等,其中,CURLOPT_URL 是必设的选项;
  4. 设置完成后,调用curl_easy_perform函数发送数据;
  5. 数据发送完毕后,调用curl_easy_cleanup清空句柄;
  6. 调用curl_global_cleanup()做清理工作。

3. 基本函数

  1. curl_global_init()
1
2
3
4
5
6
7
8
9
CURLcode curl_global_init(long flags);//初始化libcurl
函数只能调用一次,如果在curl_easy_init调用时还没调用,将有libcurl库自动调用
虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init
参数:flags
CURL_GLOBAL_ALL //初始化所有的可能的调用。
CURL_GLOBAL_SSL //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32 //初始化win32套接字库。
CURL_GLOBAL_NOTHING //没有额外的初始化
CURL_GLOBAL_DEFAULT //同时初始化SSL和Win32
  1. void curl_global_cleanup()
1
2
3
void curl_global_cleanup(void)
结束libcurl使用时,用来对curl_globl_init做的工作清理。
虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init
  1. char *curl_version()
1
打印当前libcurl库的版本。
  1. curl_easy_init()
1
2
3
函数得到 easy interface型指针
curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理.
一般curl_easy_init意味着一个会话的开始. 它会返回一个easy_handle(CURL*对象), 一般都用在easy系列的函数中.
  1. curl_easy_cleanup()
1
2
3
void curl_easy_cleanup(CURL *handle);
//释放内存
这个调用用来结束一个会话.与curl_easy_init配合着用.
  1. curl_easy_setopt()
1
2
3
4
5
6
7
8
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
//设置传输选项
几乎所有的curl 程序都要频繁的使用它.它告诉curl库.程序将有如何的行为. 比如要查看一个网页的html代码等.(这个函数有些像ioctl函数)
参数:
1. CURL类型的指针
2. 各种CURLoption类型的选项.(都在curl.h库里有定义,man 也可以查看到)
3. parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.
CURLoption 这个参数的取值很多.具体的可以查看man手册.
  1. curl_easy_perform()
1
2
3
CURLcode curl_easy_perform(CURL *handle);
//函数完成传输任务
这个函数在初始化CURL类型的指针 以及curl_easy_setopt完成后调用. 就像字面的意思所说perform就像是个舞台.让我们设置的option 运作起来.

4. curl_easy_perform 函数说明(error 状态码)

  1. CURLE_OK //任务完成一切都好
  2. CURLE_UNSUPPORTED_PROTOCOL //不支持的协议,由 URL 的头部指定
  3. CURLE_COULDNT_CONNECT //不能连接到 remote 主机或者代理
  4. CURLE_REMOTE_ACCESS_DENIED //访问被拒绝
  5. CURLE_HTTP_RETURNED_ERROR //Http 返回错误
  6. CURLE_READ_ERROR //读本地文件错误

    要获取详细的错误描述字符串,可以通过const char *curl_easy_strerror(CURLcode errornum)这个函数取得.

5. libcurl 使用的 HTTP 消息头

当使用libcurl发送http请求时,它会自动添加一些http头。我们可以通过CURLOPT_HTTPHEADER属性手动替换、添加或删除相应的HTTP消息头。
| | |
|:—|:—|
|Host|http1.1(大部分 http1.0)版本都要求客户端请求提供这个信息头。
|Pragma|”no-cache”。表示不要缓冲数据。
|Accept|”/“。表示允许接收任何类型的数据。
|Expect|以 POST 的方式向 HTTP 服务器提交请求时,libcurl 会设置该消息头为”100-continue”,它要求服务器

在正式处理该请求之前,返回一 个”OK”消息。如果 POST 的数据很小,libcurl 可能不会设置该消息头。
HTTP 协议提供了消息头,请求消息头用于告诉服务器如何处理请求;响应消息头则告诉浏览器如何处理接收到的数据。

6. 获取 http 应答头信息

   发出http请求后,服务器会返回应答头信息和应答数据,如果仅仅是打印应答头的所有内容,则直接可以通过curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, 打印函数)的方式来完成。

   需要获取的是应答头中特定的信息,比如应答码、cookies列表等,则需要通过下面这个函数:
   CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
   info参数就是我们需要获取的内容,下面是一些参数值:
   1.CURLINFO_RESPONSE_CODE
   获取应答码
   2.CURLINFO_HEADER_SIZE
   头大小
   3.CURLINFO_COOKIELIST
   cookies列表

   除了获取应答信息外,这个函数还能获取curl的一些内部信息,如请求时间、连接时间等等。

程序员的自我修养

  • 南桥芯片接低速设备的 ISA 总线。
  • 北桥芯片接高速设备的 PCI(E)总线+南桥芯片。

CPU 频率很难提升,遂多 CPU 和多核出现。多核处理器是 SMP(symmetrical Multi-Processing)的简化版。

  1. 预编译:gcc -E mian.c -o main.i
  2. 编译:gcc -S main.i -o main.s
  3. 汇编:gcc -c main.s -o main.o
  4. 链接:gcc -o main main.o

生成共享文件 gcc -fPIC -shared -o xx.so xx.c

shared 产生共享对象
PIC 地址无关代码

  • 预处理器:会扩展源代码,插入所有用#include 命令指定的文件,并扩展所有的宏。
  • 編译器:产生源文件的汇编代码。
  • 汇编器:会将汇编代码转化成二进制目标代码文件/或者说目标文件。
  • 链接器:将目标文件与实现标准库函数(如 printf)的代码合并,并产生最终的可执行文件。

静态链接

链接就是将几个输入目标文件,加工并合并在一起组成一个输出文件的过程。

静态链接主要有两个步骤:空间及地址分配、符号解析与重定位

空间和地址分配

  • 扫描所有输入目标文件,获得各段长度、属性和位置,并收集所有符号表中的符号定义和符号引用,放在一个统一的全局符号表内。
  • 将输入文件中相同性质的段合并成输出文件的一个段,比如将输入文件所有的“.text”段合并成输出文件的“.text”。
  • 为输出文件各段确定空间地址。空间地址有两种,一种是各段在可执行文件中的偏移地址,另一个是指进程的虚拟空间地址,链接过程主要关注虚拟空间地址。(这个过程实际上就是指定可执行文件与进程执行时的虚拟地址空间的映射关系)
  • 由符号在段中的偏移值,确定各符号定义的虚拟地址。

符号解析与重定位

  • 进行符号解析: 将各符号引用与输入文件某一个确定的符号定义联系在一起,如果找不到就报未定义错误。从普通程序员角度看,符号解析占链接的主要部分。
  • 根据重定位表和符号定义的地址,确定每一个重定位入口的地址。
    输入文件进行符号引用处,由于不知道相应符号的地址,所以需要将符号标记下来进行重定位,这个记录表就成为重定位表,或者重定位段。重定位表实质上是一个具有特定结构的数组,每一个数组元素都包含几点信息,重定位入口的偏移值、符号和重定位入口的类型。 -输入文件中每一个引用了外部符号的段都对应一个重定位段,如代码段“.text”对应的重定位段叫“.rel.text”.
  • 指令地址修正。
    由于不同 cpu 的寻址方式不同,所以每一个重定位入口处的实际地址都需要修正。指令修正由两个要素决定,该重定位入口的类型以及其虚拟地址。入口类型决定了指令修正方式,主要有绝对寻址修正和相对寻址修正两种。

COMMON 块

当不同的文件目标需要的 COMMON 块空间大小不一致时,以最大的为准

一个弱符号定义在多个目标文件中,而类型不同

  • 1.两个或两个以上强符号类型不一致(多个强符号定义非法)
  • 2.有一个强符号,其他都是弱符号(输出结果和强符号相同)
  • 3.两个或两个以上的弱符号类型不一致(以输入文件最大的为准)

连接器不支持符号类型,无法判断各个符号类型是否一致

ccglDd89.o: In function ‘PanaSearchCameras’

error: ld returned 1 exit status

链接时失败, pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a
在编译中要加 -lpthread 参数

1
gcc thread.c -o thread -lpthread
1
2
3
4
5
6
7
8
9
#main.c
#include <stdio.h>
int main(int argc, char *argv)
{
printf("%d\n", argc);
for(int i = 0; i < argc; i ++)
printf("%s\n", argv[i]);
return 0;
}
  • gcc main.c -o main
  • ./main 11 22 33
1
2
3
4
5
4
./main
11
22
33

所有分配出来的栈空间的每一个字节被初始化为0xCC,0xCCCC汉字编码为“

c++primer 2022/12/5

负数取余问题

1
2
unsigned char b = -1;
b = 255;

如果 a 和 d 是两个自然数,d 非零,可以证明存在两个唯一的整数 q 和 r,满足 a=qd+r 且 0 ≤ r < d;两个数取余,余数总是为正数。

5%3=3x1+2,商为 1,余数为 2;
(-5)%(-3)=(-3)x2+1,商为 2,余数为 1;
5%(-3)=(-3)x(-1)+2,商为-1,余数为 2;
(-5)%3=3x(-2)+1,商为-2,余数为 1;

如果 a 与 d 是整数,d 非零,那么余数 r 满足这样的关系:a = qd + r , q 为整数,且 0 ≤ |r| < |d|;

5%(-3) = (-3)x(-1)+2 = (-3)x(-2)-1;
如果正余数为 r1,负余数为 r2,那么有 r1 = r2 + d;
所有语言和计算器都遵循了尽量让商尽量靠近 0 的原则,即 5%(-3) 的结果为 2 而不是-1,(-5)%3 的结果是-2 而不是 1。

转义字符 输出“1”

1
cout << "\"1\"" << endl;

(1)‘a ‘, L’a’, “a”, L”a” //字符字面值,宽字符字面值,字符串字面值,宽字符串字面值;

(2) 10, 10u, 10L, 10uL, 012, 0xC //整形字面值,无符号整形字面值,长整形字面值,无符号长整形字面值,八进制整形字面值,十六进制整形字面值;

(3) 3.14, 3.14f, 3.14L //浮点型字面值,单精度浮点型字面值,扩展精度浮点型字面值;

(4) 10, 10u, 10., 10e-2 //整形字面值,无符号整形字面值,浮点型字面值,浮点型字面值。

杨辉三角

一个特性,它的第 n+1 行第 m+1 列的元素值为 C(n,m);
n!/(n - m)!m!

作用域

1
2
3
4
5
6
7
8
int main()
{
int i = 100;
for (int i = 0; i < 1; i++)
cout << i << endl; //0
cout << i << endl; //100
return 0;
}

第三章 字符串、向量、数组

命名空间 using 声明

1
using namespace std;

标准库类型string

可变长的字符序列
string 对象的下标运算符可用于访问已存在的元素,不可以用于添加元素

1
2
3
4
5
6
7
//初始化
string s1;
string s2(s1);
string s2 = s1; 等同于s2(s1)
string s3("value");
string s3 = "value"; 等同于s3("value")
string s4(n, 'c'); 把s4初始为连续n个c组成的串

string 操作

1
2
3
4
5
6
7
8
9
10
11
os << s           //将s写到输出流os中,返回os
is >> s //从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s) //从is中读取一行赋给s,返回is
s.empty() //s为空返回true,否则返回false ,返回bool类型
s.size() //返回s中字符的个数
s[n] //返回s中第n个字符的引用,位置n从0计起
s1 + s2 //返回s1和s2连接后的结果
s1 = s2 //用s2中的副本替代s1中原来的字符
s1 == s2 //判s1和s2等不等
s1 = s2 //对大小写敏感
<, <=, >, >= //利用字符在字典中的顺序进行比较

cctype 头文件中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
isalnum(c)    //c全为数字或数字式时为真
isalpha(c) //c全为字母时为真
iscntrl(c) //c是控制字符时为真
isdigit(c) //当c是数字时为真
isgragh(c) //当c不是空格但可打印时为真
islower(c) //当c不是小写字母时为真
isprint(c) //当c是可打印字符时为真
ispunct(c) //当c是标点符号时为真
isspace(c) //当c是空白时为真
isupper(c) //当c为大写字母时为真
isxdigit(c) //当c是十六进制数字时为真
tolower(c) //c输出对应的小写字母
toupper(c) //输出对应的大写字母

读取未知量的string对象

1
2
3
4
5
6
int main()
{
string word;
while(cin >> word) cout << word << endl;
return 0;
}

getline 读取一整行

1
2
3
4
5
6
int main()
{
string line;
while(getline(cin, line)) cout << line << endl;
return 0;
}

empty 函数 返回一个布尔值

1
2
3
4
5
6
7
8
int main()
{
string line;
while(getline(cin, line))
if(!line.empty()) //判断是否为空行
cout << line << endl;
return 0;
}

size 函数 返回string长度

1
2
3
4
5
6
7
8
int main()
{
string line;
while(getline(cin, line))
if(line.size() > 10) //判断长度是否大于10
cout << line << endl;
return 0;
}

stringsize函数返回值为size_type类型
string比较时采用字典序
string相加时不能字面值直接相加

c++primer 2022/12/6

标准库类型vector

标准库类型 vector 表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。vector 能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的 vector。(如果不确定元素的确切个数,使用 vector 而不使用数组)

1
2
//头文件
#include <vector>
1
2
3
4
//初始化
vector<int> v1(10, 1) //10个元素,初值为1;
vector<int> v2{10, 1} //2个元素,分别为10, 1;
vector<string> v3{10} //10个元素,花括号内的值类型和元素类型相同为列表初始化;
1
2
3
4
5
6
7
8
9
10
//vector操作
v.empty() 判空
v.size() 返回v的个数
v.push_back(t) 向v末尾添加t
v[n] 返回v的第n个引用
v1 = v2 用v2的元素拷贝替换v1
v1 = {a, b, c} 用列表元素拷贝替换v1
v1 == v2 当且仅当元素个数及相应元素值相同时相等
v1 != v2
<, <=, >, >= 以字典序比较

vector对象的下标运算符可用于访问已存在的元素,不可以用于添加元素

迭代器

所有标准库容器都可以使用迭代器

1
2
3
4
5
6
7
//运算符
*iter //返回迭代器所指元素的引用
iter->mem //解引用iter并获取该元素的名为mem的成员,等价于*iter.mem
++iter //令iter指示容器中的下一个元素
--iter //令iter指示容器中的上一个元素
iter1 == iter2 //判等
iter1 != iter2 //判不等
1
2
3
4
5
6
7
//运算
iter + n
iter - n
iter += n
iter -= n
iter1 - iter2 //两迭代器之间的距离,注意是没有加法运算的迭代器之间。
>, >=, <, <= //实质是比较两迭代器间的位置
1
2
3
4
5
6
//类型
vector<int>::iterator it1; //知道类型的定义方式、可读写
string::iterator it2;

vector<int>::const_iterator it3;
string::const_iterator it4; //只读

beginend

begin:返回指向的第一个元素的迭代器。
end:返回容器“尾元素的下一个元素”的迭代器。
如果容器为空,则beginend返回的是同一个迭代器,都是尾后迭代器。

begin的具体返回类型由对象是否是常量决定,如果对象是常量,beginend返回const_iterator;如果不是常量,返回iterator。(选用auto自行决定返回类型比较保险)

数组

数组的大小固定(使用下标访问时必须严格检查是否在合理范围内)

1
2
3
//字符数组的特殊性:可以使用字符串字面值进行初始化,但是结尾处有一处空字符。
char a1[] = {'C', '+', '+'}; //无空字符
char a2[] = "C++" //结尾默认生成空字符'\0',(为了容纳空字符,故如果规定大小的话,大小必须为4以上)
1
2
3
4
//不允许拷贝和赋值
int a[] = {o,1,2}; //正确
int b[] = a; //错误
a2 = a; //错误
1
2
3
4
5
//数组的指针与引用
int *ptrs[10]; //ptrs是含有10个整型指针的数组
int &refs[10] = ...; //错误:不存在引用的数组
int (*Parray)[10] = &arr; //Parray指向一个含有十个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组

相比于vector来说,数组的缺点

  • 不能向数组增加元素,数组大小固定,不灵活。
  • vector 可以更好地支持标准库 std。
1
2
3
4
5
6
7
8
//指针+数组
string nums[] = {"one", "two", "three"};
string *p = &nums[0]; //p指向nums的第一个元素
string *p2 = nums; //等价如上

int ia = {0,1,2,3,4};
auto ia2(ia); //ia2是一个整型指针,指向ia的第一个元素
auto ia2(&ia[0]) //等价如上

位运算符

位运算符作用于整型的运算对象,并把运算对象通过 二进制 的方式理解,并提供检查和设置二进制的功能。

运算符 功能 用法 运算规则
~ 位求反 ~ expr ~1 = 0; ~0 = 1
~1 = -2; ~-1 = 0; ~0 = -1
<< 左移 expr1 << expr2
>> 右移 expr1 >> expr2
& 位与 expr & expr 0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
^ 位异或 expr ^ expr 0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
| 位或 expr | expr 0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

sizeof 运算符

返回一条表达式或一个类型名字所占的字节数。

1
2
3
4
5
6
7
Sales_data data, *p;
sizeof(Sales_data); //Sales_data类型的对象所占的空间大小
sizeof data; //data类型的大小,结果如上
sizeof p; //指针所占的空间大小
sizeof *p; //指针所指的类型所占的空间大小,即sizeof(Sales_data)
sizeof data.revenue; //Sales_data的成员reveune对应类型的大小
sizeof Sales_data::revenue; //Sales_data的成员reveune对应类型的大小

sizeof 运算符的结果部分地依赖于其作用的类型:

  • 对 char 或者类型为 char 的表达式执行 sizeof 运算符,结果得 1。
  • 对引用类型执行 sizeof 运算得到被引用对象所占空间的大小。
  • 对指针执行 sizeof 运算得到指针本身所占空间的大小。
  • 对解引用指针执行 sizeof 运算得到指针指向的对象所占空间的大小,指针不需要有效。
  • 对数组执行 sizeof 运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次 sizeof 运算并将所得结果求和。注意,sizeof 运算不会把数组转换成指针来处理。
  • 对 string 对象或 vector 对象执行 sizeof 运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。

逗号运算符

优先级最低,先对左值进行计算,然后丢弃,真正的结果是右侧表达式的值

1
cout << (0, 1);         //输出1

try 语句块和异常处理

函数重载

重载函数(overloaded function):同一作用域内的几个函数的函数名相同,但参数列表不同。参数列表不同指的是参数类型和数量不全相同。如果只是返回类型不同,这么定义重载函数是错误的,因为大多数重载函数是根据形参来匹配相应重载函数。

main 函数不能重载

对于形参是某种类型的引用或者指针时,则通过区分其指向的是常量对象还是非常量对象以实现函数重载。这里的const是底层const

1
2
3
4
5
6
//以下4个函数的形参类型不同
void look(const int *a);
void look (int* a);

void look(const int &a);
void look(int & a);

函数匹配(function matching):是指一个把函数调用与一组重载函数中的某一个关联起来的过程,也叫重载确定(overload resolution),编译器首先将调用的实参与一组重载函数中每一个函数的参数列表进行比较,再根据比较的结果选择并调用最佳函数。

调用重载函数会有三种可能的结果:

  • 编译器找到一个与实参最佳匹配(best match)的函数,并生成调用该函数的代码。
  • 找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息。
  • 有多于一个函数可以匹配,但是每一个都不是明显的最佳选择。此时也将发生名为二义性调用的错误。

重载与作用域

  • 在不同作用域无法重载函数名,内层作用域声明的标识符会覆盖掉外层作用域的标识符
  • C++的名字查找发生在类型检查之前

内联函数和constexpr函数

  • 调用函数会导致一些时间和空间上的开销:保存调用者保存寄存器和被调用者保存寄存器,并在函数返回时恢复;将参数拷贝进栈中;栈顶指针寄存器和基址指针寄存器的存取等操作会消耗时间和空间。
  • 内联函数(inline function):在每个调用点上“内联地”展开的函数,不会产生常规函数时间和空间上的开销,与宏类似。
  • 内联函数的声明:在函数声明前面加上关键字inline
  • 内联函数适用于优化规模较小、流程直接、频繁调用的函数。
1
2
3
4
inline const string & shorterString(const string &s1, const string & s2)
{
return s1.size() <= size() ? s1 : s2;
}

constexpr函数(constexpr function): 是指能用于常量表达式的函数。

定义 constexpr 函数的要求

  • 函数的返回类型和所有形参的类型都必须是字面值类型,并且返回类型前还要加上关键字 constexpr
  • 函数体中有且只有一条 return 语句;
  • 函数体中的语句除了 return 语句外,其它语句必须是在运行时不执行任何操作的,例如空语句、声明类型别名、 using 声明等语句;

constexpr函数被隐式地指定为内联函数 ,以方便在编译过程中展开

与其他函数不同,内联函数和constexpr函数可以定义多次,但是它的多个定义必须完全一致。 所以内联函数和constexpr函数一般都定义在头文件中。

c++primer 2022/12/7

  • 类的基本思想:**数据抽象(data abstraction)封装(encapsulation)**。
  • 数据抽象:是一种依赖于**接口(interface)和实现(implementation)**分离的编程(以及设计)技术。
  • 类的接口:包含了用户所能执行的操作。
  • 类的实现:包含了类的数据成员,负责接口实现的函数体以及定义类所需的各种私有函数。
  • 封装:实现了类的接口实现的分离,隐藏了类的实现细节,使得用户只能使用类的接口而无法查看其实现细节。

基本知识

函数的声明、调用、定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

void simple(); //函数的声明

int main(void)
{
cout << "main() will call the simple() function:" << endl;
simple(); //函数调用
return 0;
}

void simple(void) //函数定义
{
cout << "I'm but a simple function" << endl;
}

函数参数和按值传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

using namespace std;

void swap(int, int); //函数的声明
void swap(int*, int*);
int a = 10, b = 20;
int main(void)
{
swap(a, b); //函数调用
swap(&a, &b);
return 0;
}
void swap(int a1, int b1) //函数定义
{
a1 ^= b1 ^= a1 ^= b1;
cout << a << " " << b << endl;//10 20
}
void swap(int* a1, int* b1) //函数定义
{
*a1 ^= *b1 ^= *a1 ^= *b1;
cout << a << " " << b << endl;//20 10
}

函数和数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

using namespace std;

const int ArSize = 8;
int sum_arr(int arr[], int);
int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };
int main(void)
{
int sum = sum_arr(cookies, ArSize);
cout << sum << endl;
cout << cookies[7] << endl; //129
return 0;
}
int sum_arr(int arr[], int n)
{
int total = 0;
for (int i = 0; i < n; i++)
total += arr[i];
cout << arr << endl << cookies << endl;
arr[7] = 129;
return total;
}

const常量保证值不能被改变,其实保证的是常量指向的内存地址所保存的数据不能被修改
‘基本数据类型‘的 值就保存在内存地址中,所以 const 定义的 ‘基础数据类型’ 不可被改变。

而引用数据类型指向的内存地址只是一个指针,通过指针来指向实际数据,也就是说,不可被改变的是指针,而不是数据,所以 const 定义的 ”引用数据类型的常量可以通过属性来修改值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

int main(void)
{
int n = 10, m = 100;
const int* p = &m;
int* const q = &m;
const int* const t = &m;
p = &n;
cout << p << " " << *p << endl; //00000063EA4FFAE4 10
*q = n;
cout << "*q = " << *q << endl; //10
cout << "q = " << q << endl; //m的地址
return 0;
}

用到的一些函数

1.fopen()

FILE *fopen(const char *filename, const char *mode)

filename – 字符串,表示要打开的文件名称。

mode – 字符串,表示文件的访问模式,可以是以下表格中的值

模式 描述
“Y” 打开一个用于读取的文件。该文件必须存在。
“w” 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
“a” 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
“r+” 打开一个用于更新的文件,可读取也可写入。该文件必须存在。
“w+” 创建一个用于读写的空文件。
“a+” 打开一个用于读取和追加的文件。

2.sscanf() //从字符串读取格式化输入

int sscanf(const char *str, const char *format, ...)

str – 这是 C 字符串,是函数检索数据的源。

format – 这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符 和 format 说明符。
format 说明符形式为 [=%[*][width][modifiers]type=]
|参数|描述|
|:—|:—|
|*| 这是一个可选的星号,表示数据是从流 stream 中读取的,但是可以被忽视,即它不存储在对应的参数中。|
|width|这指定了在当前读取操作中读取的最大字符数。|
|modifiers|为对应的附加参数所指向的数据指定一个不同于整型(针对 d、i 和 n)、无符号整型(针对 o、u 和 x)或浮点型(针对 e、f 和 g)的大小: h :短整型(针对 d、i 和 n),或无符号短整型(针对 o、u 和 x) l :长整型(针对 d、i 和 n),或无符号长整型(针对 o、u 和 x),或双精度型(针对 e、f 和 g) L :长双精度型(针对 e、f 和 g)|
|type|一个字符,指定了要被读取的数据类型以及数据读取方式。具体参见下一个表格。|

类型 合格的输入 参数类型
c 单个字符:读取下一个字符。如果指定了一个不为 1 的宽度 width,函数会读取 width 个字符,并通过参数传递,把它们存储在数组中连续位置。在末尾不会追加空字符。 char *
d 十进制整数:数字前面的 + 或 - 号是可选的。 int *
e,E,f,g,G 浮点数:包含了一个小数点、一个可选的前置符号 + 或 -、一个可选的后置字符 e 或 E,以及一个十进制数字。两个有效的实例 -732.103 和 7.12e4 float *
o 八进制整数。 int *
s 字符串。这将读取连续字符,直到遇到一个空格字符(空格字符可以是空白、换行和制表符)。 char *
u 无符号的十进制整数。 unsigned int *
x,X 十六进制整数。 int *

附加参数 – 这个函数接受一系列的指针作为附加参数,每一个指针都指向一个对象,对象类型由 format 字符串中相应的 % 标签指定,参数与 % 标签的顺序相同。

成功返回的是成功转换的值的个数

3.fprintf() //发送格式化输出到流 stream 中。

int fprintf(FILE *stream, const char *format, ...)

stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了流。

format – 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。

4.curl_easy_perform() //curl_easy_perform - 执行阻止文件传输

CURLcode curl_easy_perform(CURL *easy_handle)

curl_easy_init和所有curl_easy_setopt调用后调用此函数,并按照选项中所述执行传输。必须使用与返回的curl_easy_init调用相同的输入easy_handle调用它。libcurl将尝试在后续传输中重复使用相同的连接

5.curl_easy_strerror() //返回描述错误代码的字符串

const char *curl_easy_strerror(CURLcode errornum);

返回一个字符串,描述在参数 errornum 中传递的 CURLcode 错误代码。

6.fclose() //关闭流 stream。刷新所有的缓冲区。

int fclose(FILE *stream)

stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了要被关闭的流。

如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。

7:pthread_mutex_init() //初始化锁变量 mutex。

pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t *attr)
attr 为锁属性,NULL 值为默认属性。

8:pthread_mutex_lock() //加锁

pthread_mutex_lock(pthread_mutex_t *mutex)

9:pthread_mutex_tylock() //加锁,但是与 8 不一样的是当锁已经在使用的时候,返回为 EBUSY,而不是挂起等待。

pthread_mutex_tylock(pthread_mutex_t *mutex)

10:pthread_mutex_unlock() //释放锁

pthread_mutex_unlock(pthread_mutex_t *mutex)

11:pthread_mutex_destroy() //使用完后释放

pthread_mutex_destroy(pthread_mutex_t *mutex)

还有一些没用的代码分析

int HttpClientGlobalInit(enum TransmitMode transmit_type);//初始化函数,参数为发送信息方式

int CURL_GlobalInit();//libcurl 初始化

curl_global_init(CURL_GLOBAL_DEFAULT);//同时初始化 SSL 和 Win32

int HttpClientInit(struct HttpWrapperInfo* wrap);

  1. 判断参数 wrap 是否为空

  2. CheckWrapperConfigure(wrap);判断wrap->TransmitType 传输方式是否合法?0:1
    !=0 返回异常

  3. wrap->Timeout = TIMEOUT_DEFAULT;//最大通信时长 60

  4. wrap->TransmitType 判断发送方式 0:libcurl 1:socket

  5. CURL_Init(wrap); //调用初始化函数

    1. struct HttpCurl* sturt_curl = &(wrap->LibraryData.curl_conf);
      //curl 接口需要的信息
      //共用 union LibraryConf LibraryData struct HttpCurl curl_conf;//libcurl 发送时,用 HttpCurl 收发
    2. 判断sturt_curl->Curl != NULL , curl_easy_cleanup(sturt_curl->Curl);//结束一个 libcurl 简易句柄
    3. CURL* curl = NULL; //定义 CURL
    4. curl = curl_easy_init(); //创建一个 libcurl 句柄,启动 libcurl easy 会话
    5. if (curl == NULL) //判断 curl 初始化,失败返回 return CURL_INIT_ERR;133
    6. static int DataSheetListInit(struct DataNodeList *list) //初始化 Boundary 数据接收队列
      1. pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));//生成互斥锁
      2. ret = pthread_mutex_init(mutex, NULL); //互斥锁初始化,使用默认的互斥锁属性,默认属性为快速互斥锁

        //函数成功完成之后会返回零,其他任何返回值都表示出现了错误。失败,释放 free(mutex);返回 ret
      3. list->Mutex = mutex;
      4. list->BundaryDataMaxSize = MAX_BUNDARY_DATA_SIZE; //设置 Bundary 数据的大小,1024*1024
        list->BoudaryQueueSize = QUEUE_SIZE_32; //队列大小的设定,31
      5. list->WriteCount = 0; //接收到的 Boudary 数据数
        list->ReadCount = 0; //传递到上位的 Boudary 数据数
    7. 判断返回值 curl_easy_cleanup(curl);return CURL_QUEUE_INIT_ERR; //134
    8. sturt_curl->Curl = curl;
    9. wrap->OutRet = 0; //输出状态
    10. curl_init_num++;
  6. wrap->Send = CURL_Send; //libcul 中的收发处理,发送

    1. struct HttpCurl* curl_sturt = &(wrap->LibraryData.curl_conf);

    2. CURL* curl = curl_sturt->Curl;

    3. 判断 curl 是否为空

    4. int CheckCurlConfigure(struct HttpWrapperInfo* wrap) //检查配置

      1. if (!strlen(wrap->Url)) return CURL_URL_EMPTY; //128 Url 空白
      2. 判断传输模式 get post;认证类型 0:None 1:Digest 2:Basic //0:None 1:Digest 2:Basic 129 发送设定错误
      3. 判断 Id 和 password
        if (wrap->Authentication != NONE_MODE && !(strlen(wrap->Id) && strlen(wrap->Password))) return CURL_ID_OR_PASSWORD_EMPTY;
      4. 判断 multipart 和输出
        if (wrap->Multipart == 0 && (wrap->OutputType < OUT_FILE_MODE || wrap->OutputType > OUT_BUF_MODE )) return CURL_RECEPTION_CONFIGURE_ERR;
      5. 判断互斥锁
        if (wrap->LibraryData.curl_conf.QueueList.Mutex == NULL && wrap->Multipart) return CURL_LOCK_ERR;
    5. CURLcode res = 0;

    6. char* url_data = (char*)malloc(MAX_URL_LEN); //准备向 libcurl 发送的数据

    7. if (url_data == NULL) return CURL_MALLOC_ERR;

    8. res = SetHTTPConfig(wrap, url_data, 0);

      //url_data url 或数据缓存区域 1. if(sign) 0 2. switch (wrap->SendType) 1. Get 1. url_data 初始化 2. memcpy(url_data, wrap->Url, strlen(wrap->Url)); //指定 Url 3. 判断 if (wrap->BufDataLen > 0)
      memcpy(&url_data[strlen(wrap->Url)], “?”, 1);
      memcpy(&url_data[strlen(wrap->Url) + 1], wrap->BufData, wrap->BufDataLen); 4.
      res |= curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); //CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPGET, long useget);
      CURLOPT_HTTPGET - 请求 HTTP GET 请求,如果 useget 为 1.这将强制 HTTP 请求恢复使用 GET。 5.
      res |= curl_easy_setopt(curl, CURLOPT_URL, url_data); //CURLcode curl_easy_setopt(CURL *handle, CURLOPT_URL, char *URL);
      CURLOPT_URL - 此传输的网址,传入指向要使用的 URL 的指针。该参数应为以 null 结尾的字符串的 char *,该字符串必须按以下格式进行 URL 编码:方案://主机:端口/路径 2. Post 1. res |= curl_easy_setopt(curl, CURLOPT_POST, 1L); 2. res |= curl_easy_setopt(curl, CURLOPT_URL, wrap->Url); 3. 判断 if (wrap->BufDataLen > 0)
      memset(url_data, 0, MAX_URL_LEN);
      memcpy(url_data, wrap->BufData, wrap->BufDataLen);
      res |= curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url_data); 3. if(wrap->Authentication && (wrap->Id != NULL && wrap->Password != NULL))

      //判断认证类型, ID, password
      res |= curl_easy_setopt(curl, CURLOPT_USERNAME, wrap->Id); //传入Id
      res |= curl_easy_setopt(curl, CURLOPT_PASSWORD, wrap->Password); //传入password 4. switch (wrap->Authentication) //判断认证类型 1. None
      break 2. digest
      res |= curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); //CURLOPT_HTTPAUTH - 要尝试的 HTTP 服务器身份验证方法 //CURLAUTH_DIGEST, HTTP 摘要式身份验证。通过公共网络进行身份验证 3. basic
      res |= curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); //CURLAUTH_BASIC, HTTP 基本身份验证。以纯文本形式通过网络发送用户名和密码,其他人可以轻松捕获。 5.

        
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      curl_sturt->HeaderBufPos = 0; //头部缓冲区结束位置
      memset(curl_sturt->HeaderBuf, 0, CURL_HEADER_BUFF);
      res |= curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteHttpHeaderCallback);
      //CURLOPT_HEADERFUNCTION-接收标头数据的回调
      //CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HEADERFUNCTION,
      // header_callback);
      //size_t header_callback(char *buffer,
      // size_t size,
      // size_t nitems,
      // void *userdata);
      // userdata 的指针是使用 CURLOPT_HEADERDATA 选项设置的指针。
      // 若不返回每次接收的数据大小,回调函数只会执行一次,然后直接跳出回调函数,
      // 导致 HTTP 接收失败。
      `
      1. size_t WriteHttpHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userptr)
      <br>//回调函数
      ` 1. int str_position = GetMatchingStr(curl_sturt->HeaderBuf,
      HttpBoundaryHeadStr,
      curl_sturt->HeaderBufPos);
      //字符串匹配
      2. res |= curl_easy_setopt(curl, CURLOPT_HEADERDATA, wrap);
    9. memset(wrap->BoundaryString, 0, MAX_BOUNDARY_HEAD);

    10. if (res) free(url_data); return CURL 设置异常;

    11. switch (wrap->OutputType)//输出到文件或缓存区

      1. OUT_FILE_MODE //文件
        1. wrap->Fp = fopen( wrap->Filename, “a+”);
        2. if (wrap->Fp == NULL) return 打开文件失败
      2. OUT_BUF_MODE //缓冲区
        1. wrap->BufPosition = 0; //缓冲区初始化
        2. if (wrap->Buf == NULL) return 缓冲区未指定
        3. memset(wrap->Buf, 0, wrap->BufMaxlength);
    12. res = curl_easy_perform(curl); //curl_easy_perform - 执行阻塞文件传输

    13. printf(“%s \r\n”, curl_sturt->HeaderBuf);

    14. while() GetMatchingStr

    15. if (res != CURLE_OK && res != CURLE_AGAIN) //输出错误信息

      1. fprintf(stderr, “curl_easy_perform() failed: %s\n”,
        curl_easy_strerror(res));
      2. err = CURL_HTTP_INTERACTIVE_ERR; //HTTP 发送失败
    16. err = HTTPReturnProcessing(wrap, url_data, http_res);
      //http 返回数据包处理

    17. free(url_data);
      //从输出类型到收发后的继续处理

    18. switch (wrap->OutputType)

      1. OUT_FILE_MODE //文件
        1. fclose(wrap->Fp) //关闭文件流
      2. OUT_BUF_MODE //缓冲区
        1. break;
  7. wrap->Read = CURL_Read; //libcul 中的收发处理,读

    1. if (!wrap->Multipart) //不是 multipart 的情况
    2. return ListReadData(&((wrap->LibraryData.curl_conf).QueueList),buf);
      //获取 Boundary 数据 1. pthread_mutex_lock(list->Mutex); //等待互斥对象上的锁定 2. memcpy(buf, list->TempData.Data, list->TempData.DataLen); //复制数据 3. data_len = list->TempData.DataLen; //数据长度 4. memset(list->TempData.Data, 0, list->TempData.DataLen); //清除数据 5. list->TempData.DataLen = 0; 6. pthread_mutex_unlock(list->Mutex); //释放 7. return data_len;
  8. wrap->GetDataLen = CURL_GetDataLen; //获取数据个数。

    1. if (!wrap->Multipart) //判断读取模式
    2. if(timeout == 0)
    3. return ListReadData(&((wrap->LibraryData.curl_conf).QueueList),buf);
      //获取 Boundary 数据 1. pthread_mutex_lock(list->Mutex); //等待互斥对象上的锁定 2. memcpy(buf, list->TempData.Data, list->TempData.DataLen); //复制数据 3. data_len = list->TempData.DataLen; //数据长度 4. memset(list->TempData.Data, 0, list->TempData.DataLen); //清除数据 5. list->TempData.DataLen = 0; 6. pthread_mutex_unlock(list->Mutex); //释放 7. return data_len;

PamaAPI 初始化

  1. int PanaInit(struct PanaCamera *camera, struct PanaConfigInfo pana_config)

    1. if (strlen(camera->IpAddr))

      //判断是否初始化 - return PANA_INTERFACE_INIT_ERR; - HttpClientGlobalInit(pana_config.TransmitType);

    2. memset(camera, 0, sizeof(struct PanaCamera));
      //面向松下相机的库构造体

    3. pthread*mutex_t _mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));

      //生成互斥锁

    4. ret = pthread_mutex_init(mutex, NULL);

      //mutex 初始化

    5. if (ret == -1) free(mutex);return PANA_MUTEX_INIT_ERR;

      //判断返回值

    6. pthread*mutex_t _mutex1 = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));

      //mutex1 生成

    7. if(mutex1 == NULL) free(mutex);return PANA_MALLOC_ERR;

      //判断 mutexl

    8. ret = pthread_mutex_init(mutex1, NULL);

      //mutex 初始化

    9. if (ret == -1)
      pthread_mutex_destroy(mutex);free(mutex);free(mutex1);return PANA_MUTEX_INIT_ERR;

    10. for (; i < 2; i++)
      //调用 HTTP_Client_Init - camera->Wrapper[i].TransmitType = pana_config.TransmitType; - camera->Wrapper[i].Authentication = pana_config.Authentication; - camera->Wrapper[i].Timeout = pana_config.Timeout;

      1. ret = HttpClientInit(&camera->Wrapper[i]);
      <br>//成功返回0
      2. if(ret)
          - if(i == 1)    HttpClientClean(&camera->Wrapper[0]);
              1. if(wrap == NULL) return WRAPPER_FUN_PARAMETED;
              <br>//wrap的判断,如果为空,则作为异常返回
              2. switch (wrap->TransmitType)
              <br>//发送方式判断
                  - CURL_MODE
                      1. err = CURL_Clean(wrap);
                      <br>//libcul中的收发处理
                          - struct HttpCurl* curl_sturt = &(wrap->LibraryData.curl_conf);
                          - if (curl_sturt == NULL)   return -1;
                          - [curl_easy_cleanup(curl_sturt->Curl)](#curl_easy_cleanup);
                          - ListClean(&curl_sturt->QueueList);
                              1. [pthread_mutex_destroy(list->Mutex)](#pthread_mutex_destroy);
                              <br>//mutex释放
                              2. free(list->Mutex);
          - break;
      3. memcpy(camera->Wrapper[i].Id, pana_config.UserId, strlen(pana_config.UserId));
      4. memcpy(camera->Wrapper[i].Password, pana_config.Password, strlen(pana_config.Password));
      
    11. if(ret)

      //判断初始化是否成功 - pthread_mutex_destroy(mutex); free(mutex); - pthread_mutex_destroy(mutex1); free(mutex1); - memset(camera, 0, sizeof(struct PanaCamera)); - return PANA_INTERFACE_INIT_ERR;

    12. camera->CommandMutex = mutex;

      camera->StreamMutex = mutex1;

    13. memcpy(camera->IpAddr, pana_config.Ip, strlen(pana_config.Ip));

      camera->PortNum = pana_config.Port;

    14. return PANA_SUCCESS;





获取 UID

  1. int PanaGetUid(struct PanaCamera* camera, char *uid, int *buf_len, int *http_status)

    1. struct PanaCommandParam params[2];
    2. sprintf(buf_data, “FILE=2 vcodec=jpeg”);
      • params[0].Id = PANA_GETUID_FILE;//0
      • params[1].Id = PANA_GETUID_VCODEC;//1
      • memset(params[0].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
      • memset(params[1].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
    3. int n = sscanf(buf_data, “FILE=%s vcodec=%s”, params[0].Value, params[1].Value);
    4. printf(“%d , %s , %s \r\n”, n, params[0].Value, params[1].Value);
    5. if(!n) return PANA_PARAMETER_MATCHING_FAILED;
    6. int ret = PanaCameraSendApi(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_GETUID, PANA_COMMAND_INTERFACE, http_status,NULL);

      //数据发送处理

      //PANA_GETUID = 0;PANA_COMMAND_INTERFACE = 0;
      if(state)//0
    7. if(ret) return ret;
    8. n = sscanf(&data[5], “UID=%s\r\n”, uid_data);
    9. if(n == 0) return PANA_DATA_MATCHING_FAILED;
    10. memcpy(uid, uid_data, strlen(uid_data));
    11. *buf_len = strlen(uid_data);;//PanaApi-321
    12. return PANA_SUCCESS;




启动流媒体接收,JPEG 图像发送(开始)

  1. int PanaStartStreaming(struct PanaCamera* camera, char* uid, double framerate, DataCallBack call_back, int *http_status)
    1. struct PanaCommandParam params[3];
    2. sprintf(buf_data, “connect=start framerate=%g UID=%s”,framerate, uid);
    • params[0].Id = PANA_CONNECT;//3
    • params[1].Id = PANA_JPEG_FRAMERATE;//4
    • params[2].Id = PANA_UID;//5
    1. for(; i < sizeof(params) / sizeof(struct PanaCommandParam); i++)
      • memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
    2. int n = sscanf(buf_data, “connect=%s framerate=%s UID=%s”, params[0].Value, params[1].Value,params[2].Value);
    3. if(!n) return PANA_PARAMETER_MATCHING_FAILED;
    4. int ret = PanaCameraSendApi(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_JPEGSTREAMING, PANA_DATA_STREAM, http_status,call_back);

      //PANA_JPEGSTREAMING = 1;PANA_DATA_STREAM = 1;

      //数据发送处理
      if(state)//!0
    5. if(ret) return ret;
    6. return PANA_SUCCESS;




JPEG 图像传送(已结束),停止接收流式传输

  1. int PanaStopStreaming(struct PanaCamera* camera, char* uid, int* http_status);
    1. struct PanaCommandParam params[2];
      2.sprintf(buf_data, “connect=stop UID=%s”, uid);
      • params[0].Id = PANA_CONNECT;
      • params[1].Id = PANA_UID;
    2. memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
    3. int n = sscanf(buf_data, “connect=%s UID=%s”, params[0].Value, params[1].Value);

      //connect=params[0].Value UID=params[1].Value -> buf_data;
    4. if(!n) return PANA_PARAMETER_MATCHING_FAILED;
    5. int ret = PanaCameraSendApi(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_JPEGSTREAMING, PANA_COMMAND_INTERFACE, http_status,NULL);

      //PANA_JPEGSTREAMING = 1, PANA_COMMAND_INTERFACE = 0; 1. if(state)//0
    6. if (ret) return ret;
    7. return PANA_SUCCESS;




JPEG 画像 1shot 要求, one shot 画像取得

  1. int PanaGetOneShotJpeg(struct PanaCamera* camera, char* bitmap, int *bmp_len, int *http_status)
    1. int ret = PanaCameraSendApi(camera, data, 0, NULL, PANA_GETONESHOTJPEG, PANA_COMMAND_INTERFACE, http_status,NULL);

      //PANA_GETONESHOTJPEG = 2, PANA_COMMAND_INTERFACE = 0 1. if(state)//0
    2. if(ret) return ret;
    3. if(bitmap == NULL) return PANA_PARAMETER_BUFFER_EMPTY;//7

      //获取画像数据
    4. memcpy(bitmap, data, camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition);
    5. *bmp_len = camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition;
    6. return PANA_SUCCESS;




设定照明的亮度

  1. int PanaSetLight(struct PanaCamera* camera, int value, int* http_status)
    1. struct PanaCommandParam params[1];
    2. sprintf(buf_data, “brightness=%d”, value);

      //value 照明的亮度
    3. params[0].Id = PANA_LIGHT_BRIGHTNESS;
    4. memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
    5. int n = sscanf(buf_data, “brightness=%s”, params[0].Value);
    6. if(!n) return PANA_PARAMETER_MATCHING_FAILED;//5
    7. int ret = PanaCameraSendApi(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_SETLIGHT, PANA_COMMAND_INTERFACE, http_status,NULL);

      //PANA_SETLIGHT = 12, PANA_COMMAND_INTERFACE = 0;
    8. if (ret) return ret;
    9. return PANA_SUCCESS;




获取照相机设备的信息

  1. int PanaGetInfo(struct PanaCamera* camera, struct PanaDictionary** dict, int *num, int *http_status)
    1. struct PanaCommandParam params[1];
    2. sprintf(buf_data, “FILE=1”);
    3. params[0].Id = PANA_GETUID_FILE;
    4. memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
    5. int n = sscanf(buf_data, “FILE=%s”, params[0].Value);
    6. if(!n) return PANA_PARAMETER_MATCHING_FAILED;//5
    7. int ret = PanaCameraSendApi(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_GETINFO, PANA_COMMAND_INTERFACE, http_status,NULL);

      //PANA_GETINFO = 7, PANA_COMMAND_INTERFACE = 0;
    8. if(ret) retrun ret;
    9. int str_num = GetStrNum(data, “\r\n”, camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition);

      //获取数据的个数 1. 搜索对象在数据中的个数








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
1. pthread_mutex_t *mutex = NULL;
2. - if(state) mutex = camera->StreamMutex;
- else mutex = camera->CommandMutex;
3. if (mutex == NULL) return PANA_LOCK_NOT_INIT;
4. pthread_mutex_lock(mutex);
<br>//上锁
5. - camera->Wrapper[state].SendType = GET_MODE;
- memset(camera->Wrapper[state].Url, 0, MAX_URL_LEN);
- memcpy(camera->Wrapper[state].Url, "http://", strlen("http://"));
- memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], camera->IpAddr, strlen(camera->IpAddr));
6. if(camera->PortNum) sprintf(str_port, ":%d", camera->PortNum);
7. - memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], str_port, strlen(str_port));
- memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], PanaCmdStr[cmd], strlen(PanaCmdStr[cmd]));
8. camera->Wrapper[state].OutputType = OUT_BUF_MODE;
9. camera->Wrapper[state].Buf = data;
10. camera->Wrapper[state].BufMaxlength = MAX_BUNDARY_DATA_SIZE;
11. if(num > 0)
- for (; j < num - 1; j ++) sprintf(&buf_data[strlen(buf_data)], "%s=%s&",PanaParameterStr[params[j].Id], params[j].Value);
- sprintf(&buf_data[strlen(buf_data)], "%s=%s",PanaParameterStr[params[num - 1].Id], params[num - 1].Value);
12. - camera->Wrapper[state].BufData = buf_data;
- camera->Wrapper[state].BufDataLen = strlen(buf_data);
13. if(state)//!0
1. for(; j < num; j ++)
- if(params[j].Id == PANA_JPEG_FRAMERATE) framerate = params[j].Value;
- if(params[j].Id == PANA_UID) uid = params[j].Value;
2. 线程的设置
- struct [PanaCallBack](#PanaCallBack) pthread_struct;
- pthread_struct.Camera = camera;
- pthread_struct.Fun = call_back;
- pthread_struct.FrameRate = framerate;
- pthread_struct.Uid = uid;
3. 启动线程
- ret = [pthread_create](#pthread_create)(&camera->KeepAliveThread, NULL, ReadDataStreamPthread, &pthread_struct);
1. ReadDataStreamPthread
<br>//线程启动地址
1. [struct PanaCallBack](#PanaCallBack)* pana_call_back = (struct PanaCallBack*)arg;
2. time_t start,end;
<br>//time_t 这种类型就是用来存储从1970年到现在经过了多少秒
3. struct PanaCamera* camera = pana_call_back->Camera;
4. while(camera->Wrapper[PANA_DATA_STREAM].Multipart)
<br>//通过循环处理接收数据
- end = time(0)
- int time_keep = end - start;
- if(time_kep > 30)
1. ret = PanaSendKeepAlive(camera, pana_call_back->Uid, &httpstatus);
<br>//heatbeat发送
1. struct PanaCommandParam params[3];
2. sprintf(buf_data, "mode=jpeg protocol=http UID=%s", uid);
- params[0].Id = PANA_KEEP_ALIVE_MODE;//7
- params[1].Id = PANA_KEEP_ALIVE_PROTOCOL;//8
- params[2].Id = PANA_UID;//5
3. for(; i < sizeof(params) / sizeof(struct PanaCommandParam); i++)
- memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
4. if(!n) return PANA_PARAMETER_MATCHING_FAILED;
5. int ret = [PanaCameraSendApi](#PanaCameraSendApi)(camera, data, sizeof(params) / sizeof(struct PanaCommandParam), params, PANA_SENDKEEPALIVE, PANA_COMMAND_INTERFACE, http_status,NULL);
[if(state)//0](#if(state)//0)
<br>//PANA_SENDKEEPALIVE = 3;PANA_COMMAND_INTERFACE = 0;
6. if(ret) return ret;
7. return PANA_SUCCESS;
2. start = time(0); time_keep = 0;

- int len = camera->Wrapper[PANA_DATA_STREAM].GetDataLen (&camera->Wrapper[PANA_DATA_STREAM], 30 - time_keep > 10 ? 10 : 30 - time_keep);
- if(len == -1) sleep(2);
- else if(len)
1. memset(data, 0 ,MAX_BUNDARY_DATA_SIZE);
2. ret = camera->Wrapper[PANA_DATA_STREAM].Read(&camera->Wrapper[PANA_DATA_STREAM], data);
3. if(ret == len) pana_call_back->Fun(data,ret);
else
- else printf("read error\r\n");break;
14. ret = camera->Wrapper[state].Send(&camera->Wrapper[state]);
<br>//发送数据
15. if(state)//!0
1. camera->Wrapper[state].Multipart = 0;
2. pthread_join(camera->KeepAliveThread,NULL);
<br>//等待线程停止
3. camera->KeepAliveThread = 0;
16. *httpStatus = camera->Wrapper[state].StatusCode;
17. pthread_mutex_unlock(mutex);
<br>//解锁
18. if(ret) return PANA_TASK_FAILED;
19. return PANA_SUCCESS;

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
1. pthread_mutex_t *mutex = NULL;
2. - if(state) mutex = camera->StreamMutex;
- else mutex = camera->CommandMutex;
3. if (mutex == NULL) return PANA_LOCK_NOT_INIT;
4. pthread_mutex_lock(mutex);
<br>//上锁
5. - camera->Wrapper[state].SendType = GET_MODE;
- memset(camera->Wrapper[state].Url, 0, MAX_URL_LEN);
- memcpy(camera->Wrapper[state].Url, "http://", strlen("http://"));
- memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], camera->IpAddr, strlen(camera->IpAddr));
6. if(camera->PortNum) sprintf(str_port, ":%d", camera->PortNum);
7. - memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], str_port, strlen(str_port));
- memcpy(&camera->Wrapper[state].Url[strlen(camera->Wrapper[state].Url)], PanaCmdStr[cmd], strlen(PanaCmdStr[cmd]));
8. camera->Wrapper[state].OutputType = OUT_BUF_MODE;
9. camera->Wrapper[state].Buf = data;
10. camera->Wrapper[state].BufMaxlength = MAX_BUNDARY_DATA_SIZE;
11. if(num > 0)
- for (; j < num - 1; j ++) sprintf(&buf_data[strlen(buf_data)], "%s=%s&",PanaParameterStr[params[j].Id], params[j].Value);
- sprintf(&buf_data[strlen(buf_data)], "%s=%s",PanaParameterStr[params[num - 1].Id], params[num - 1].Value);
12. - camera->Wrapper[state].BufData = buf_data;
- camera->Wrapper[state].BufDataLen = strlen(buf_data);
13. if(state)//0 or [if(state)//!0](#if(state)//!0)
14. ret = camera->Wrapper[state].Send(&camera->Wrapper[state]);
<br>//发送数据
15. if(state)//0
16. *httpStatus = camera->Wrapper[state].StatusCode;
17. pthread_mutex_unlock(mutex);
<br>//解锁
18. if(ret) return PANA_TASK_FAILED;
19. return PANA_SUCCESS;

1
2
3
4
5
6
7
8
9
struct PanaCamera {
struct HttpWrapperInfo Wrapper[2];
char IpAddr[PANA_MAX_URL_LENGTH];
int PortNum;
pthread_t KeepAliveThread;
pthread_mutex_t *CommandMutex;
pthread_mutex_t *StreamMutex;
char Uid[PANA_MAX_LEN_OF_UID];
} PanaCamera;

1
2
3
4
void curl_easy_cleanup(CURL * handle)
//结束一个libcurl简易句柄
//此函数必须是调用简易会话的最后一个函数。
//curl_easy_cleanup杀死句柄和与之关联的所有内存!

1
2
pthread_mutex_destroy(pthread_mutex_t *mutex)
//mutex使用完后释放

1
2
3
4
5
//存储命令的参数信息
struct PanaCommandParam {
enum PanaCmdParameterDefine Id;
char Value[PANA_MAX_LEN_OF_PARAMETER_VALUE];
};

1
int PanaCameraSendApi(struct PanaCamera* camera,char *data, int num,struct PanaCommandParam* params, enum PanaCmdDefine cmd, enum PanaCameraInterface state, int* httpStatus, DataCallBack call_back)

1
2
3
4
5
6
7
//回调函数的数据
struct PanaCallBack {
struct PanaCamera *Camera;
DataCallBack Fun;
char *FrameRate;
char *Uid;
};

1
2
3
4
5
6
7
#include <pthread.h>
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void *)(*start_rtn)(void *), void *arg);
编译链接 -lpthread
第一个参数为指向线程 标识符的 指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数

1
2
3
4
5
6
7
//回调函数的数据
struct PanaCallBack {
struct PanaCamera *Camera;
DataCallBack Fun;
char *FrameRate;
char *Uid;
};

1
2
//JPEG图像发送(开始)
int PanaStartStreaming(struct PanaCamera* camera, char* uid, double framerate, DataCallBack call_back, int * http_status);

1
2
//停止接收流式传输
int PanaStopStreaming(struct PanaCamera* camera, char* uid, int* http_status)

相机检索处理

int PanaSearchCameras(char* host_ip_addr, char* host_mac_addr, struct CameraInfo*_ camera_info, int_ num)

//发送接口配置

- memset(&addr_send, 0, sizeof(struct sockaddr_in));
- addr_send.sin_family = AF_INET;
- addr_send.sin_addr.s_addr = htonl(INADDR_BROADCAST);//代表255.255.255.255的广播地址
- addr_send.sin_port = htons(PANA_HOST_PORT);

//配置任务需要传出的存储地址,数量,以及需要匹配的 IP

- pthread_data.CameraInfo = camera_info;
- pthread_data.Num = num;

//lookup 分组的生成

- int lookuplen = CreateLookupData(host_ip_addr, host_mac_addr, lookup);//lookup数据的生成
    - int data_len = CreateUDPDataLookup(&buf[sizeof(struct PanaUDPRequestHead)]);
        - struct PanaUDPRequestLookup lookup;
        - lookup.RequestCode = htons(LookuoData.RequestCode)//将一个无符号短整型数值转换为网络字节序
        - lookup.RequestDataLength = htons(LookupData.RequestDataLength)
        - memcpy(lookup.RequestData, LookupData.RequestData, sizeof(lookup.RequestData));
        - memcpy(buf, &lookup, sizeof(struct PanaUDPRequestLookup));
        - return sizeof(struct PanaUDPRequestLookup);
    - struct PanaUDPRequestHead head = CreateUDPRequestHead(LOOKUP, host_ip_addr, host_mac_addr, "00-00-00-00-00-00", data_len);
        - struct PanaUDPRequestHead reqhead;
        - memset(&reqhead, 0, sizeof(struct PanaUDPRequestHead));//清空
        - if (type < LOOKUP || type >= MAX_PANAUDPDATATYPE) return reqhead;//检查数据包类型
        + reqhead.Version = htons(1);
        + reqhead.Length = htons(datalen);
        + reqhead.Operation = htons(PanaUDPRequestHeadSize[type].Size);
        - 检查是否有本地IP  将host_ip_addr输入到reqhead.HostIP
        - 检查是否有本地MAC  将host_mac_addr输入到reqhead.HostMac
        - 检查是否有目标MAC  将client_mac_addr输入到reqhead.CameraMac
        //对应的封装参数的设定
        - reqhead.PacketNo = htons(PanaUDPRequestHeadSize[type].PacketNo);
        - reqhead.UserIDSettingState = PanaUDPRequestHeadSize[type].UserIDSettingState;
        //固定数据
        - memcpy(reqhead.Identifier, Identifier, sizeof(reqhead.Identifier));
        - reqhead.Reserved1 = 0;
        - reqhead.MinorVersion = 2;
        - reqhead.Model = 1;
        - memset(reqhead.Reserved2, 0, sizeof(reqhead.Reserved2));
        return reqhead;
    - int head_len = CreateUDPDataHead(head, buf);//lookup数据头的创建
        - 标头固定格式数据写入
        - memcpy(buf, (char *)&head,   sizeof(struct PanaUDPRequestHead));
        - return  sizeof(struct PanaUDPRequestHead);
    - unsigned short checksum = 0xffff;
    - memcpy(&buf[sizeof(struct PanaUDPRequestHead) + data_len], (char*)&checksum, sizeof(checksum));
    - checksum = CheckDataPacket(buf, sizeof(struct PanaUDPRequestHead) + data_len + 2);
        - for (; i < num; i++) checksum = (checksum + (data[i] & 0xff)) & 0xffff;
        - checksum = (checksum + 1) & 0xffff;
        - return checksum;
    - data_ushort = htons(checksum);
    - memcpy(&buf[sizeof(struct PanaUDPRequestHead) + data_len + 2], (char*)&data_ushort, sizeof(data_ushort));
    - 数据头长度的检查if(head.Version)  return data_len + head_len + PacketTailLen;
    - memset(buf, 0, data_len + head_len + PacketTailLen);
    - return 0;
    - memcpy((char *)&pthread_data.HostIp, &lookup[offsetof(struct PanaUDPRequestHead, HostIP)], 4);
    - ret = InitSocketInterface(&send_fd, &recv_fd, 0);//UDP端口初始化
        - struct sockaddr_in addr_recv;
        - struct timeval tv;

        - //阻塞接收最大阻塞时间
        + tv.tv_sec = 10;
        + tv.tv_usec = 0;
        + addr_recv.sin_family = AF_INET;
        + addr_recv.sin_port = htons(PANA_CAMERA_PORT);
        + addr_recv.sin_addr.s_addr = INADDR_ANY;//0.0.0.0,本机所有IP

        - *recv_fd = socket(AF_INET, SOCK_DGRAM, 0);
        - *send_fd = socket(AF_INET, SOCK_DGRAM, 0);
        - ret = bind(*recv_fd, (struct sockaddr *)&addr_recv, sizeof(struct sockaddr_in));
        - ret = setsockopt(*send_fd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));//SO_BROADCAST:允许发送广播数据
        - if(recv_type)  ret = setsockopt(*recv_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));//SO_RCVTIMEO:接收超时
    - if(ret) close...

//线程接收接口

- pthread_data.RecvFd = recv_fd;
- ret = pthread_create(&thread, NULL, ReadDataInformPthread, &pthread_data);//创建线程
- if(ret) close...

//发送 lookup

- ret = UdpSendData(send_fd, lookup, lookuplen, &addr_send, 4, 3);
-       UdpSendData(int fd, char* data, int data_len, struct sockaddr_in* addrto, int send_interval, int send_num)
    - while(send_num)
        - while(send_len > 0)
            - ret = sendto(fd, data_ptr, send_len, 0, (struct sockaddr *)addrto, sizeof(struct sockaddr_in));//发送成功的字节数
            - send_len = send_len - ret;
            - data_ptr += ret;
        - sleep(send_interval);
        - send_num--;
        - data_ptr = data;
        - send_len = data_len;
    - return ret;
- if(ret == -1) ...

//等待停止接收

- pthread_join(thread ,NULL);
- close(recv_fd);
- close(send_fd);
- return 0;




面向照相机网络设定的数据封装

1
2
3
4
5
6
7
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};