实习
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 | int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); |
\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 | 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. 流程
- 在主线程中调用
curl_global_init(CURL_GLOBAL_ALL)
初始化 - 调用
curl_easy_init
获取一个句柄; - 调用
curl_easy_setopt
函数设置此次传输的一些基本参数,如 url 地址、http 头、cookie 信息、发送超时时间等,其中,CURLOPT_URL 是必设的选项; - 设置完成后,调用
curl_easy_perform
函数发送数据; - 数据发送完毕后,调用
curl_easy_cleanup
清空句柄; - 调用
curl_global_cleanup()
做清理工作。
3. 基本函数
- curl_global_init()
1 | CURLcode curl_global_init(long flags);//初始化libcurl |
- void curl_global_cleanup()
1 | void curl_global_cleanup(void) |
- char *curl_version()
1 | 打印当前libcurl库的版本。 |
- curl_easy_init()
1 | 函数得到 easy interface型指针 |
- curl_easy_cleanup()
1 | void curl_easy_cleanup(CURL *handle); |
- curl_easy_setopt()
1 | CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter); |
- curl_easy_perform()
1 | CURLcode curl_easy_perform(CURL *handle); |
4. curl_easy_perform 函数说明(error 状态码)
CURLE_OK
//任务完成一切都好CURLE_UNSUPPORTED_PROTOCOL
//不支持的协议,由 URL 的头部指定CURLE_COULDNT_CONNECT
//不能连接到 remote 主机或者代理CURLE_REMOTE_ACCESS_DENIED
//访问被拒绝CURLE_HTTP_RETURNED_ERROR
//Http 返回错误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)的简化版。
- 预编译:gcc -E mian.c -o main.i
- 编译:gcc -S main.i -o main.s
- 汇编:gcc -c main.s -o main.o
- 链接: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 | #main.c |
- gcc main.c -o main
- ./main 11 22 33
1 | 4 |
所有分配出来的栈空间的每一个字节被初始化为0xCC
,0xCCCC
汉字编码为“烫
”
c++primer 2022/12/5
负数取余问题
1 | unsigned char b = -1; |
如果 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 | int main() |
第三章 字符串、向量、数组
命名空间 using
声明
1 | using namespace std; |
标准库类型string
可变长的字符序列
string 对象的下标运算符可用于访问已存在的元素,不可以用于添加元素
1 | //初始化 |
string 操作
1 | os << s //将s写到输出流os中,返回os |
cctype 头文件中的函数
1 | isalnum(c) //c全为数字或数字式时为真 |
读取未知量的
string
对象
1 | int main() |
getline
读取一整行
1 | int main() |
empty
函数 返回一个布尔值
1 | int main() |
size
函数 返回string
长度
1 | int main() |
string
类size
函数返回值为size_type
类型string
比较时采用字典序string
相加时不能字面值直接相加
c++primer 2022/12/6
标准库类型vector
标准库类型 vector 表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。vector 能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的 vector。(如果不确定元素的确切个数,使用 vector 而不使用数组)
1 | //头文件 |
1 | //初始化 |
1 | //vector操作 |
vector
对象的下标运算符可用于访问已存在的元素,不可以用于添加元素
迭代器
所有标准库容器都可以使用迭代器
1 | //运算符 |
1 | //运算 |
1 | //类型 |
begin
和end
begin
:返回指向的第一个元素的迭代器。end
:返回容器“尾元素的下一个元素”的迭代器。
如果容器为空,则begin
和end
返回的是同一个迭代器,都是尾后迭代器。
begin
的具体返回类型由对象是否是常量决定,如果对象是常量,begin
和end
返回const_iterator
;如果不是常量,返回iterator
。(选用auto
自行决定返回类型比较保险)
数组
数组的大小固定(使用下标访问时必须严格检查是否在合理范围内)
1 | //字符数组的特殊性:可以使用字符串字面值进行初始化,但是结尾处有一处空字符。 |
1 | //不允许拷贝和赋值 |
1 | //数组的指针与引用 |
相比于
vector
来说,数组的缺点
- 不能向数组增加元素,数组大小固定,不灵活。
- vector 可以更好地支持标准库 std。
1 | //指针+数组 |
位运算符
位运算符作用于整型的运算对象,并把运算对象通过 二进制 的方式理解,并提供检查和设置二进制的功能。
运算符 | 功能 | 用法 | 运算规则 |
---|---|---|---|
~ |
位求反 | ~ 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 | Sales_data data, *p; |
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 | //以下4个函数的形参类型不同 |
函数匹配(function matching):是指一个把函数调用与一组重载函数中的某一个关联起来的过程,也叫重载确定(overload resolution),编译器首先将调用的实参与一组重载函数中每一个函数的参数列表进行比较,再根据比较的结果选择并调用最佳函数。
调用重载函数会有三种可能的结果:
- 编译器找到一个与实参最佳匹配(best match)的函数,并生成调用该函数的代码。
- 找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息。
- 有多于一个函数可以匹配,但是每一个都不是明显的最佳选择。此时也将发生名为二义性调用的错误。
重载与作用域
- 在不同作用域无法重载函数名,内层作用域声明的标识符会覆盖掉外层作用域的标识符
- C++的名字查找发生在类型检查之前
内联函数和
constexpr
函数
- 调用函数会导致一些时间和空间上的开销:保存调用者保存寄存器和被调用者保存寄存器,并在函数返回时恢复;将参数拷贝进栈中;栈顶指针寄存器和基址指针寄存器的存取等操作会消耗时间和空间。
- 内联函数(inline function):在每个调用点上“内联地”展开的函数,不会产生常规函数时间和空间上的开销,与宏类似。
- 内联函数的声明:在函数声明前面加上关键字
inline
。- 内联函数适用于优化规模较小、流程直接、频繁调用的函数。
1 | inline const string & shorterString(const string &s1, const string & s2) |
constexpr
函数(constexpr function): 是指能用于常量表达式的函数。
定义 constexpr 函数的要求
- 函数的返回类型和所有形参的类型都必须是字面值类型,并且返回类型前还要加上关键字 constexpr
- 函数体中有且只有一条 return 语句;
- 函数体中的语句除了 return 语句外,其它语句必须是在运行时不执行任何操作的,例如空语句、声明类型别名、 using 声明等语句;
constexpr
函数被隐式地指定为内联函数 ,以方便在编译过程中展开
与其他函数不同,内联函数和
constexpr
函数可以定义多次,但是它的多个定义必须完全一致。 所以内联函数和constexpr
函数一般都定义在头文件中。
c++primer 2022/12/7
类
- 类的基本思想:**数据抽象(data abstraction)和封装(encapsulation)**。
- 数据抽象:是一种依赖于**接口(interface)和实现(implementation)**分离的编程(以及设计)技术。
- 类的接口:包含了用户所能执行的操作。
- 类的实现:包含了类的数据成员,负责接口实现的函数体以及定义类所需的各种私有函数。
- 封装:实现了类的接口和实现的分离,隐藏了类的实现细节,使得用户只能使用类的接口而无法查看其实现细节。
基本知识
函数的声明、调用、定义
1 | #include <iostream> |
函数参数和按值传递
1 | #include <iostream> |
函数和数组
1 | #include <iostream> |
const
常量保证值不能被改变,其实保证的是常量指向的内存地址所保存的数据不能被修改
‘基本数据类型‘的 值就保存在内存地址中,所以 const 定义的 ‘基础数据类型’ 不可被改变。
而引用数据类型指向的内存地址只是一个指针,通过指针来指向实际数据,也就是说,不可被改变的是指针,而不是数据,所以 const 定义的 ”引用数据类型的常量可以通过属性来修改值。
1 | #include <iostream> |
用到的一些函数
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);
判断参数 wrap 是否为空
CheckWrapperConfigure(wrap);
判断wrap->TransmitType
传输方式是否合法?0:1
!=0 返回异常wrap->Timeout = TIMEOUT_DEFAULT;
//最大通信时长 60wrap->TransmitType
判断发送方式 0:libcurl 1:socketCURL_Init(wrap);
//调用初始化函数struct HttpCurl* sturt_curl = &(wrap->LibraryData.curl_conf);
//curl 接口需要的信息
//共用 union LibraryConf LibraryData struct HttpCurl curl_conf;//libcurl 发送时,用 HttpCurl 收发- 判断
sturt_curl->Curl != NULL
,curl_easy_cleanup(sturt_curl->Curl);
//结束一个 libcurl 简易句柄 CURL* curl = NULL;
//定义 CURLcurl = curl_easy_init();
//创建一个 libcurl 句柄,启动 libcurl easy 会话if (curl == NULL)
//判断 curl 初始化,失败返回 return CURL_INIT_ERR;133static int DataSheetListInit(struct DataNodeList *list)
//初始化 Boundary 数据接收队列pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
//生成互斥锁ret = pthread_mutex_init(mutex, NULL);
//互斥锁初始化,使用默认的互斥锁属性,默认属性为快速互斥锁
//函数成功完成之后会返回零,其他任何返回值都表示出现了错误。失败,释放 free(mutex);返回 retlist->Mutex = mutex;
list->BundaryDataMaxSize = MAX_BUNDARY_DATA_SIZE;
//设置 Bundary 数据的大小,1024*1024list->BoudaryQueueSize = QUEUE_SIZE_32;
//队列大小的设定,31list->WriteCount = 0;
//接收到的 Boudary 数据数list->ReadCount = 0;
//传递到上位的 Boudary 数据数
- 判断返回值 curl_easy_cleanup(curl);return CURL_QUEUE_INIT_ERR; //134
- sturt_curl->Curl = curl;
- wrap->OutRet = 0; //输出状态
- curl_init_num++;
wrap->Send = CURL_Send; //libcul 中的收发处理,发送
struct HttpCurl* curl_sturt = &(wrap->LibraryData.curl_conf);
CURL* curl = curl_sturt->Curl;
判断 curl 是否为空
int CheckCurlConfigure(struct HttpWrapperInfo* wrap) //检查配置
- if (!strlen(wrap->Url)) return CURL_URL_EMPTY; //128 Url 空白
- 判断传输模式 get post;认证类型 0:None 1:Digest 2:Basic //0:None 1:Digest 2:Basic 129 发送设定错误
- 判断 Id 和 password
if (wrap->Authentication != NONE_MODE && !(strlen(wrap->Id) && strlen(wrap->Password))) return CURL_ID_OR_PASSWORD_EMPTY; - 判断 multipart 和输出
if (wrap->Multipart == 0 && (wrap->OutputType < OUT_FILE_MODE || wrap->OutputType > OUT_BUF_MODE )) return CURL_RECEPTION_CONFIGURE_ERR; - 判断互斥锁
if (wrap->LibraryData.curl_conf.QueueList.Mutex == NULL && wrap->Multipart) return CURL_LOCK_ERR;
CURLcode res = 0;
char* url_data = (char*)malloc(MAX_URL_LEN); //准备向 libcurl 发送的数据
if (url_data == NULL) return CURL_MALLOC_ERR;
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.2. res |= curl_easy_setopt(curl, CURLOPT_HEADERDATA, wrap);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20curl_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);
//字符串匹配memset(wrap->BoundaryString, 0, MAX_BOUNDARY_HEAD);
if (res) free(url_data); return CURL 设置异常;
switch (wrap->OutputType)//输出到文件或缓存区
- OUT_FILE_MODE //文件
- wrap->Fp = fopen( wrap->Filename, “a+”);
- if (wrap->Fp == NULL) return 打开文件失败
- OUT_BUF_MODE //缓冲区
- wrap->BufPosition = 0; //缓冲区初始化
- if (wrap->Buf == NULL) return 缓冲区未指定
- memset(wrap->Buf, 0, wrap->BufMaxlength);
- OUT_FILE_MODE //文件
res = curl_easy_perform(curl); //curl_easy_perform - 执行阻塞文件传输
printf(“%s \r\n”, curl_sturt->HeaderBuf);
while() GetMatchingStr
if (res != CURLE_OK && res != CURLE_AGAIN) //输出错误信息
- fprintf(stderr, “curl_easy_perform() failed: %s\n”,
curl_easy_strerror(res)); - err = CURL_HTTP_INTERACTIVE_ERR; //HTTP 发送失败
- fprintf(stderr, “curl_easy_perform() failed: %s\n”,
err = HTTPReturnProcessing(wrap, url_data, http_res);
//http 返回数据包处理free(url_data);
//从输出类型到收发后的继续处理switch (wrap->OutputType)
- OUT_FILE_MODE //文件
- fclose(wrap->Fp) //关闭文件流
- OUT_BUF_MODE //缓冲区
- break;
- OUT_FILE_MODE //文件
wrap->Read = CURL_Read; //libcul 中的收发处理,读
- if (!wrap->Multipart) //不是 multipart 的情况
- 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;
wrap->GetDataLen = CURL_GetDataLen; //获取数据个数。
- if (!wrap->Multipart) //判断读取模式
- if(timeout == 0)
- 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 初始化
int PanaInit(struct PanaCamera *camera, struct PanaConfigInfo pana_config)
if (strlen(camera->IpAddr))
//判断是否初始化 - return PANA_INTERFACE_INIT_ERR; - HttpClientGlobalInit(pana_config.TransmitType);memset(camera, 0, sizeof(struct PanaCamera));
//面向松下相机的库构造体pthread*mutex_t _mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
//生成互斥锁ret = pthread_mutex_init(mutex, NULL);
//mutex 初始化if (ret == -1) free(mutex);return PANA_MUTEX_INIT_ERR;
//判断返回值pthread*mutex_t _mutex1 = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
//mutex1 生成if(mutex1 == NULL) free(mutex);return PANA_MALLOC_ERR;
//判断 mutexlret = pthread_mutex_init(mutex1, NULL);
//mutex 初始化if (ret == -1)
pthread_mutex_destroy(mutex);free(mutex);free(mutex1);return PANA_MUTEX_INIT_ERR;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));
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;camera->CommandMutex = mutex;
camera->StreamMutex = mutex1;memcpy(camera->IpAddr, pana_config.Ip, strlen(pana_config.Ip));
camera->PortNum = pana_config.Port;return PANA_SUCCESS;
获取 UID
int PanaGetUid(struct PanaCamera* camera, char *uid, int *buf_len, int *http_status)
- struct PanaCommandParam params[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);
- int n = sscanf(buf_data, “FILE=%s vcodec=%s”, params[0].Value, params[1].Value);
- printf(“%d , %s , %s \r\n”, n, params[0].Value, params[1].Value);
- if(!n) return PANA_PARAMETER_MATCHING_FAILED;
- 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 - if(ret) return ret;
- n = sscanf(&data[5], “UID=%s\r\n”, uid_data);
- if(n == 0) return PANA_DATA_MATCHING_FAILED;
- memcpy(uid, uid_data, strlen(uid_data));
- *buf_len = strlen(uid_data);;//PanaApi-321
- return PANA_SUCCESS;
启动流媒体接收,JPEG 图像发送(开始)
- int PanaStartStreaming(struct PanaCamera* camera, char* uid, double framerate, DataCallBack call_back, int *http_status)
- struct PanaCommandParam params[3];
- 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
- for(; i < sizeof(params) / sizeof(struct PanaCommandParam); i++)
- memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
- int n = sscanf(buf_data, “connect=%s framerate=%s UID=%s”, params[0].Value, params[1].Value,params[2].Value);
- if(!n) return PANA_PARAMETER_MATCHING_FAILED;
- 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 - if(ret) return ret;
- return PANA_SUCCESS;
JPEG 图像传送(已结束),停止接收流式传输
- int PanaStopStreaming(struct PanaCamera* camera, char* uid, int* http_status);
- struct PanaCommandParam params[2];
2.sprintf(buf_data, “connect=stop UID=%s”, uid); - params[0].Id = PANA_CONNECT;
- params[1].Id = PANA_UID;
- memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
- 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; - if(!n) return PANA_PARAMETER_MATCHING_FAILED;
- 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 - if (ret) return ret;
- return PANA_SUCCESS;
- struct PanaCommandParam params[2];
JPEG 画像 1shot 要求, one shot 画像取得
- int PanaGetOneShotJpeg(struct PanaCamera* camera, char* bitmap, int *bmp_len, int *http_status)
- 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 - if(ret) return ret;
- if(bitmap == NULL) return PANA_PARAMETER_BUFFER_EMPTY;//7
//获取画像数据 - memcpy(bitmap, data, camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition);
- *bmp_len = camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition;
- return PANA_SUCCESS;
- int ret = PanaCameraSendApi(camera, data, 0, NULL, PANA_GETONESHOTJPEG, PANA_COMMAND_INTERFACE, http_status,NULL);
设定照明的亮度
- int PanaSetLight(struct PanaCamera* camera, int value, int* http_status)
- struct PanaCommandParam params[1];
- sprintf(buf_data, “brightness=%d”, value);
//value 照明的亮度 - params[0].Id = PANA_LIGHT_BRIGHTNESS;
- memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
- int n = sscanf(buf_data, “brightness=%s”, params[0].Value);
- if(!n) return PANA_PARAMETER_MATCHING_FAILED;//5
- 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; - if (ret) return ret;
- return PANA_SUCCESS;
获取照相机设备的信息
- int PanaGetInfo(struct PanaCamera* camera, struct PanaDictionary** dict, int *num, int *http_status)
- struct PanaCommandParam params[1];
- sprintf(buf_data, “FILE=1”);
- params[0].Id = PANA_GETUID_FILE;
- memset(params[i].Value, 0, PANA_MAX_LEN_OF_PARAMETER_VALUE);
- int n = sscanf(buf_data, “FILE=%s”, params[0].Value);
- if(!n) return PANA_PARAMETER_MATCHING_FAILED;//5
- 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; - if(ret) retrun ret;
- int str_num = GetStrNum(data, “\r\n”, camera->Wrapper[PANA_COMMAND_INTERFACE].BufPosition);
//获取数据的个数 1. 搜索对象在数据中的个数
1 | 1. pthread_mutex_t *mutex = NULL; |
1 | 1. pthread_mutex_t *mutex = NULL; |
1 | struct PanaCamera { |
1 | void curl_easy_cleanup(CURL * handle) |
1 | pthread_mutex_destroy(pthread_mutex_t *mutex) |
1 | //存储命令的参数信息 |
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 | //回调函数的数据 |
1 | #include <pthread.h> |
1 | //回调函数的数据 |
1 | //JPEG图像发送(开始) |
1 | //停止接收流式传输 |
相机检索处理
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 | sockaddr_in(在netinet/in.h中定义): |