Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

C++高性能网络编程

发表于 2019-10-26 | 更新于 2024-03-20

最近需要用C++编写一个网络程序,因此将网络编程的相关知识整理学习一下。

Socket编程

Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。从设计模式的角度看来,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

Socket 还可以认为是一种网络间不同计算机上的进程通信的一种方法,利用三元组(ip地址,协议,端口)就可以唯一标识网络中的进程,网络中的进程通信可以利用这个标志与其它进程进行交互。

Socket 起源于 Unix ,Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开(open) –> 读写(write/read) –> 关闭(close)”模式来进行操作。因此 Socket 也被处理为一种特殊的文件。

Socket类型

创建 socket 的时候需要指定 socket 的类型,一般有三种:

  • SOCK_STREAM:面向连接的稳定通信,底层是 TCP 协议,我们会一直使用这个。
  • SOCK_DGRAM:无连接的通信,底层是 UDP 协议,需要上层的协议来保证可靠性。
  • SOCK_RAW:更加灵活的数据控制,能让你指定 IP 头部

Socket编程接口

  • socket():创建socket
  • bind():绑定socket到本地地址和端口,通常由服务端调用
  • listen():TCP专用,开启监听模式
  • accept():TCP专用,服务器等待客户端连接,一般是阻塞态
  • connect():TCP专用,客户端主动连接服务器
  • send():TCP专用,发送数据
  • recv():TCP专用,接收数据
  • sendto():UDP专用,发送数据到指定的IP地址和端口
  • recvfrom():UDP专用,接收数据,返回数据远端的IP地址和端口
  • closesocket():关闭socket

Socket通信流程

TCP通信流程

图1 TCP通信流程

UDP通信流程

图2 UDP通信流程

字节序

不同的计算机对数据的存储格式不一样,比如 32 位的整数 0x12345678,可以在内存里从高到低存储为 12-34-56-78 或者从低到高存储为 78-56-34-12。

但是这对于网络中的数据来说就带来了一个严重的问题,当机器从网络中收到 12-34-56-78 的数据时,它怎么知道这个数据到底是什么意思?

解决的方案也比较简单,在传输数据之前和接受数据之后,必须调用 htonl/htons 或 ntohl/ntohs 先把数据转换成网络字节序或者把网络字节序转换为机器的字节序。

  • TCP 和 UDP 的端口是互不干扰的,也就是说系统可以同时开启 TCP 80 端口和 UDP 80 端口。
  • socket 不属于任何一层网络协议,它是对 TCP 层的封装,方便网络编程。

网络编程模型

常用的服务器应用分类如下:

服务器应用分类

图3 服务器应用分类

同步阻塞迭代模型

同步阻塞迭代模型是最简单的一种IO模型,其核心代码如下:

1
2
3
4
5
6
7
8
9
bind(srvfd);
listen(srvfd);
for(;;)
{
clifd = accept(srvfd,...); //开始接受客户端来的连接
read(clifd,buf,...); //从客户端读取数据
dosomthingonbuf(buf);
write(clifd,buf)//发送数据到客户端
}

上面的程序存在如下一些弊端:

  1. 如果没有客户端的连接请求,进程会阻塞在accept系统调用处,程序不能执行其他任何操作。(系统调用使得程序从用户态陷入内核态)
  2. 在与客户端建立好一条链路后,通过read系统调用从客户端接受数据,而客户端合适发送数据过来是不可控的。如果客户端迟迟不发生数据过来,则程序同样会阻塞在read调用,此时,如果另外的客户端来尝试连接时,都会失败。
  3. 同样的道理,write系统调用也会使得程序出现阻塞(例如:客户端接受数据异常缓慢,导致写缓冲区满,数据迟迟发送不出)。

多进程并发模型

多进程并发模型在同步阻塞迭代模型的基础上进行了一些改进,以避免是程序阻塞在read系统调用上。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bind(srvfd);
listen(srvfd);
for(;;){
clifd = accept(srvfd,...); //开始接受客户端来的连接
ret = fork();
switch( ret )
{
case -1 :
do_err_handler();
break;
case 0: // 子进程
client_handler(clifd);
break ;
default : // 父进程
close(clifd);
continue ;
}
}
void client_handler(clifd)
{
read(clifd,buf,...); //从客户端读取数据
dosomthingonbuf(buf);
write(clifd,buf)//发送数据到客户端
}

上述程序在accept系统调用时,如果没有客户端来建立连接,会阻塞在accept处。一旦某个客户端连接建立起来,则立即开启一个新的进程来处理与这个客户的数据交互。避免程序阻塞在read调用,而影响其他客户端的连接。

多线程并发模型

在多进程并发模型中,每一个客户端连接开启fork一个进程,若客户端连接较大,则系统依然将不堪负重。通过多线程(或线程池)并发模型,可以在一定程度上改善这一问题。

在服务端的线程模型实现方式一般有三种:

  • 按需生成(来一个连接生成一个线程)
  • 线程池(预先生成很多线程)
  • Leader follower(LF)

以第一种为例,其核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void *thread_callback( void *args ) //线程回调函数
{
int clifd = *(int *)args ;
client_handler(clifd);
}

void client_handler(clifd)
{
read(clifd,buf,...); //从客户端读取数据
dosomthingonbuf(buf);
write(clifd,buf)//发送数据到客户端
}
bind(srvfd);
listen(srvfd);
for(;;)
{
clifd = accept();
pthread_create(...,thread_callback,&clifd);
}

服务端分为主线程和工作线程,主线程负责accept()连接,而工作线程负责处理业务逻辑和流的读取等。因此,即使在工作线程阻塞的情况下,也只是阻塞在线程范围内,对继续接受新的客户端连接不会有影响。

第二种实现方式,通过线程池的引入可以避免频繁的创建、销毁线程,能在很大程序上提升性能。

但多线程模型先天具有如下缺点:

  • 稳定性相对较差。一个线程的崩溃会导致整个程序崩溃。
  • 临界资源的访问控制,在加大程序复杂性的同时,锁机制的引入会是严重降低程序的性能。性能上可能会出现“辛辛苦苦好几年,一夜回到解放前”的情况。

IO多路复用模型

多进程模型和多线程(线程池)模型每个进程/线程只能处理一路IO,在服务器并发数较高的情况下,过多的进程/线程会使得服务器性能下降。而通过多路IO复用,能使得一个进程同时处理多路IO,提升服务器吞吐量。这是一种进程预先告知内核的能力,让内核发现进程指定的一个或多个IO条件就绪了,就通知进程。使得一个进程能在一连串的事件上等待。

IO复用的实现方式目前主要有select、poll和epoll。

select和poll的原理基本相同:

  • 注册待侦听的fd(这里的fd创建时最好使用非阻塞)

  • 每次调用都去检查这些fd的状态,当有一个或者多个fd就绪的时候返回

  • 返回结果中包括已就绪和未就绪的fd

相比select,poll解决了单个进程能够打开的文件描述符数量有限制这个问题:select受限于FD_SIZE的限制,如果修改则需要修改这个宏重新编译内核;而poll通过一个pollfd数组向内核传递需要关注的事件,避开了文件描述符数量限制。此外,select和poll共同具有的一个很大的缺点就是包含大量fd的数组被整体复制于用户态和内核态地址空间之间,开销会随着fd数量增多而线性增大。

epoll的出现,解决了select、poll的缺点:

  • 基于事件驱动的方式,避免了每次都要把所有fd都扫描一遍。

  • epoll_wait只返回就绪的fd。

  • epoll使用nmap内存映射技术避免了内存复制的开销。

  • epoll的fd数量上限是操作系统的最大文件句柄数目,这个数目一般和内存有关,通常远大于1024。

总结:

  • select:支持注册 FD_SETSIZE(1024) 个 socket。
  • poll: poll 作为 select 的替代者,最大的区别就是,poll 不再限制 socket 数量。
  • epoll:epoll 能直接返回具体的准备好的通道,时间复杂度 O(1)。

参考链接

  1. C++网络框架和库,by 帝王铠.
  2. C++ 高性能服务器网络框架设计细节,by 范蠡.
  3. 值得推荐的C/C++框架和库 (真的很强大),by tibet889.
  4. 有哪些值得推荐的c/c++开源框架与库,by 零声学院.
  5. 有什么适合提高 C/C++ 网络编程能力的开源项目推荐?,by zhihu.
  6. 网络库libevent、libev、libuv对比,by 小麒麟666.
  7. libevent、libev、libuv、IOCP、asio、muduo优劣分析、QT下编译libevent静态库,by 雒珣.
  8. Reactor 模式简介,by lotabou.
  9. IO设计模式:Reactor和Proactor对比,by 大CC.
  10. Proactor和Reactor模型,by wiseAaron.
  11. 如何深刻理解reactor和proactor?,by zhihu.
  12. Libevent 编程指南,by senlin.
  13. 网络编程(三):从libevent到事件通知机制,by auxten.
  14. 使用 libevent 和 libev 提高网络应用性能,by Martin C. Brown.
  15. 兄déi,libuv了解一下,by 宋光宇.
  16. Libuv 源码分析(2):源码一览,by Tubumu.
  17. IO设计模式:Actor、Reactor、Proactor,by _raindrop.
  18. Proactor网络编程模式歪解,by WUYU.
  19. 深入理解Reactor模式,by 独木の白帆.
  20. socket 编程基础知识,by cizixs.
  21. 一文读懂高性能网络编程中的I/O模型,by Jack Jiang.
  22. 几种典型的服务器网络编程模型归纳(select poll epoll),by DrStream.
  23. Linux IO模型和网络编程模型,by wade&luffy.
  24. 开源免费的C/C++网络库(c/c++ sockets library) 七剑下天山,by 工程师WWW.
  25. c++ 网络编程(一)TCP/UDP windows/linux 下入门级socket通信 客户端与服务端交互代码,by aircraft.

地形数据格式总结

发表于 2019-10-20 | 更新于 2019-11-15

地形数据是能够表示地球表面高低起伏状态的数据,即具有高程信息的数据。数字高程模型(DEM)是一种对空间起伏变化的连续表示方法,是一种特殊的 DatasetGrid 数据模型,每个网格的值为高程值,而且有标准的颜色表来表示,这对分幅 DEM 图像的合成很有帮助。

数字地面模型(digital terrain model,DTM)就是以数字的形式来表示实际地形特征的空间分布。有时所指的地形特征点仅指地面点的高程,就将这种数字地形描述称为数字高程模型(digital elevation model,DEM)。最初是于1958年由美国麻省理工学院Miller教授提出。数字地面模型广泛用于遥感,地理信息系统,大地测量和电子地图等领域。

常用的地形数据主要有:etopo、GTOPO30、GEBCO以及SRTM。按照精度从低到高排序如下:

  • etopo5:GMT 网格数据之 etopo5
  • etopo2:GMT 网格数据之 ETOPO2v2
  • etopo1:GMT 网格数据之 ETOPO1
  • GEBCO:GEBCO:全球水深数据
  • GTOPO30:GMT 网格数据之 GTOPO30
  • SRTM30_PLUS:全球地形起伏数据:SRTM30_PLUS
  • SRTM15_PLUS:全球地形起伏数据:SRTM15_PLUS
  • SRTM3:高精度地形网格数据 SRTM
  • ASTER GDEM:全球数字高程数据:ASTER GDEM
数据源 空间分辨率 覆盖范围 陆地/海洋
etopo5 5 弧分 全球 陆地 + 海洋
etopo2 2 弧分 全球 陆地 + 海洋
etopo1 1 弧分 全球 陆地 + 海洋
GEBCO 1 弧分 / 30 弧秒 全球 陆地 + 海洋
GTOPO30 30 弧秒 全球 陆地
SRTM30_PLUS 30 弧秒 (约 1km) 纬度 [-81,81] 陆地 + 海洋
SRTM15_PLUS 15 弧秒 (约 500m) 纬度 [-81,81] 陆地 + 海洋
SRTM 3 弧秒 (约 90m) 纬度 [-60,60] 陆地
ASTER GDEM 1 弧秒 (约 30m) 纬度 [-83,83] 陆地

ETOPO

ETOPO是一种地形高程数据。该数据由NGDC美国地球物理中心发布(U.S. National Geophysical Data Center )。与srtm、aster gdem一样,均为高程数据,所不同的是它还包括海洋海底地形数据。

ETOPO地形数据有五种规格, ETOPO1的效果最好,ETOPO2、ETOPO5数据尽管可以使用,但目前已不推荐采用。其中ETOPO1约三百多兆的压缩包, ETOPO2约九十兆。

ETOPO1

ETOPO1包含了全球地形和海洋深度,采样间隔为1弧分,是目前精度最高的global relief数据;其分为两个版本,Ice Surface和Bedrock,两个版本基本一致。不同之处在于在处理南极洲和Greenland地形时,前者给出的是加上冰盖层之后的高程,后者给出的是岩床的高程。

对于每个版本又细分为 grid-registered和cell-registered,其中grid-registered是权威版本,cell-registered是衍生版本,因而推荐下载使用grid-registered版本。

GEBCO

GEBCO数据的全称是General Bathymetric Chart of the Oceans( 全球海洋通用水深数据) ,是由国际海道测量组织(IHO) 和政府间海洋学委员会(IOC) 联合发布的最全面的世界大洋海底地形数据,也是当今海洋模式中最常用的海洋水深数据之一。

SRTM

SRTM 即航天飞机雷达地形测绘使命。航天地形测绘是指以人造地球卫星、宇宙飞船、航天飞机等航天器为工作平台,对地球表面所进行的遥感测量。以往的航天测绘由于其精度有限,一般只能制作中、小比例尺地图。SRTM则是美国太空总署(NASA)和国防部国家测绘局(NIMA)以及德国与意大利航天机构共同合作完成联合测量,由美国发射的“奋进”号航天飞机上搭载SRTM系统完成。本次测图任务从2000年2月11日开始至22日结束,共进行了11天总计222小时23分钟的数据采集工作,获取北纬60度至南纬56度之间总面积超过1.19亿平方公里的雷达影像数据,覆盖地球80%以上的陆地表面。

SRTM系统获取的雷达影像的数据量约9.8万亿字节,经过两年多的数据处理,制成了数字地形高程模型(DEM)。SRTM产品2003年开始公开发布,经历多修订,目前最新的版本为V4.1版本。

SRTM地形数据主要包含两类数据:SRTM3和SRTM1。

  • SRTM3精度为3弧秒,即90m一个点,包括非洲、北美、南美、欧亚、澳大利亚以及部分岛屿;
  • SRTM1精度为1弧秒,即30m一个点,仅限美国地区;

所有的数据被分为1度1度的单元,共计上万个数据文件。文件名代表了该单元左下角的位置;不同网站下载的数据文件名不同,但文件名类似N37W105的 数据文件,其单元左下角的坐标为(37N,105W);每个SRTM3数据文件包含了12011201个采样点;高程数据单位为m,参考水准面为 WGS84大地水准面模型;网格划分采用gridline registration方式,因而单元的南北边及左右边与相邻单元的边重合。

全国DEM 1km、500m和250m数据(SRTM 90m)

中国海拔高度(DEM)空间分布数据来源于美国奋进号航天飞机的雷达地形测绘SRTM(Shuttle Radar Topography Mission,SRTM)数据。SRTM数据有现实性强、免费获取等优点,全球许多应用研究都采用SRTM数据开展环境分析。该数据集为基于最新的SRTM V4.1数据经重采样生成,包括1km、500m和250m三种精度的全国一张图数据。数据采用WGS84椭球投影。

hgt转tiff

1
gdal_translate -of GTiff N40E120.hgt N40E120.tif

ASTER GDEM

ASTER GDEM,即先进星载热发射和反射辐射仪全球数字高程模型,与SRTM一样为数字高程DEM,其全球空间分辨率为30米。该数据是根据 NASA的新一代对地观测卫星Terra的详尽观测结果制作完成的。其数据覆盖范围为北纬83°到南纬83°之间的所有陆地区域,达到了地球陆地表面的99%。号称是“迄今最完整的全球地形数据”。

ASTER GDEM:空间分辨率:1弧度秒 (约30 米),精度:垂直精度20米,水平精度30米,SRTM数据的纬度覆盖范围是[-60,60],ASTER GDEM数据的纬度覆盖范围为[-83,83];SRTM的空间分辨率一般为90m,只有美国境内存在空间分辨率为30m的数据;ASTER GDEM的空间分辨率为30m;

参考链接

  1. 全国DEM 1km、500m和250m数据(SRTM 90m),by 资源环境数据云平台.
  2. Digital_elevation_model,by wikipedia.
  3. GMT地形数据,by YF2015.
  4. 全球地形起伏数据总结,by SeisMan.
  5. Cesium卫星地图和高程数据切片经验总结,by jackhuang.
  6. 高精度地形网格数据 SRTM,by seisman.
  7. 工具gdal2srtmtiles的安装使用,by 四域公子.
  8. 地球椭球,by wikipedia.
  9. 椭球体的重要参数和公式,by cyqian.

GDAL处理地图栅格Tif黑边的方法

发表于 2019-10-20 | 更新于 2023-02-02

通过地图下载软件LocaSpaceViewer下载卫星影像拼接成Tif文件后,会出现黑边问题。此外在Cesium中浏览瓦片地图会出现蓝色区域。下面即分析这两个问题出现的原因和解决方法。

下载方法

LocaSpaceViewer中可选择行政区域下载卫星影像,这种方式拼接后的tif文件必然会出现黑边,如图1所示。

黑边问题1

图1 黑边问题1

推荐在LocaSpaceViewer中以瓦片矩形区域下载卫星影像。具体方法为:

  1. 在该链接google-maps-coordinates-tile-bounds-projection处查询瓦片的经纬度。
  2. 在LocaSpaceViewer中绘制矩形,并将矩形顶点的经纬度设置为待下载瓦片的经纬度。
  3. 在LocaSpaceViewer下载指定瓦片。

该方法也可能会出现黑边,如图2所示。

黑边问题2

图1 黑边问题2

此外,将tif文件转换成标准瓦片后,在Cesium中浏览瓦片地图会出现蓝色区域,如图3所示。

蓝边问题

图3 蓝边问题

问题分析

对问题1,下载不规则边界的地图时,地图下载软件不会自动填充白色或变成透明,所以导致黑边问题1的产生。

对问题2,产生原因目前不清楚,但因黑边的产生很有规则,所以解决方法会比较简单。

对问题3,产生原因是蓝边对应的瓦片不存在。

解决方法

对问题1,可采用链接用LSV下载的高清地图去黑边教程指明的方法去除黑边,但使用如下命令:

1
gdal2tiles.py <image> <tilesdir>

生成瓦片时会报错,因此建议采用瓦片矩形区域下载卫星影像,不要下载行政区域卫星地图。

对问题2,使用如下命令去除黑边:

1
2
3
4
// 查看tif文件元数据信息
gdalinfo 10.tif
// 根据黑边位置使用如下命令去除
gdal_translate -srcwin 0 0 4096 4096 10.tif test.tif

对问题3,使用LocaSpaceViewer下载指定级别指定区域的卫星影像即可。

参考链接

  1. Cesium卫星地图和高程数据切片经验总结,by jackhuang.
  2. 用LSV下载的高清地图去黑边教程,by LocaSpaceViewer.
  3. cesium加载离线tms切片,by nygfcn.
  4. python gdal安装与简单使用,by mumu122.
  5. windows10 环境中安装GDAL及其python绑定,by 蔚蓝小栈.

QT集成OSG开发

发表于 2019-10-13 | 更新于 2021-08-29

Qt集成OSG开发是指使用Qt开发图形用户界面,封装OSG为Qt的Widget,然后由OSG负责三维展示的解决方案。具体方法如下:

QT+OSG开发环境配置

具体配置过程请参考Windows下QT与OSG开发环境配置。

封装OSG为Widget

封装OSG为Widget可参考osgQt。osgQt的编译链接与OSG类似。

osgQt仅将OSG封装成简单的Widget,还需研究将OSG封装成自定义控件,从而在QT Designer里方便使用。

参考链接

  1. OSG嵌入QT的简明总结,by charlee44.
  2. OSG和Qt结合,by 先锋小牛.
  3. OSG中使用Qt做界面,by 很菜但努力.
  4. 第1课时《Qt osgEarth 编程入门》,by 汀兰左岸.
  5. QT pro文件解析, by 博弈right.
  6. Windows下QT与OSG开发环境配置,by jackhuang.
  7. window下查看dll, lib文件是32位还是64位,by 剑西楼.
  8. 使用VS工具查看dll是32位还是64位,by 代码运输员.
  9. Qt自定义控件的创建与初步使用(一)之自定义控件的创建步骤,by 穿过记忆的河流.
  10. Qt自定义控件的创建与初步使用(二)之图片上绘制文字、箭头、曲线,by 穿过记忆的河流.
  11. QT自定义控件(生成和使用),by jannn.
  12. QT学习笔记(5):自定义控件,by xuzheng11332.
  13. QT自定义控件在QTCreator中不显示问题,by AleutianSix.
  14. Qt中QObject QApplication Qwidget QDialog等等都是什么关系??,by 假如明天没有太阳.
  15. Qt项目界面文件(.ui)及其作用(超详细),by C语言中文网.
  16. Qt5+VS2015开发之基础,by Leiym Blog.

SQLServer2008中日期类型数据的存储

发表于 2019-10-13 | 更新于 2019-12-30

日期类型比较

SQL Server 2008中有datetime、datetime2、datetimeoffset三种数据类型用于存储时间类型数据。它们之间的差别主要有:

  • datetime、datetime2不存储时区信息,datetimeoffset存储时区信息。
  • datetime与datetime2区别是两者存储字节大小和精度不同,后者优于前者。

因此,在存储时间信息时,优先使用datetimeoffset数据类型。当使用datetime和datetime2数据类型时,因没有时区信息,很多客户端链接库会将存储的时间默认为UTC时间。例如:

SQL Server2008中存储某人的出生日期为“1970-01-01 00:00:00”,通过sequelizejs框架读取得到出生日期将是“1970-01-01T00:00:00Z”,这里 T 仅仅是分隔日期和时间的符号,没有其他含义,而 Z 表示 UTC 时间。

这时需要根据自己所在时区对时间进行修正。最简单的方法是在SQL语句中进行修正,代码如下:

1
select birthday1, dateadd(hour,-8,birthday) from students

日期转换

有时时间会以字符串类型存储在数据库中,当需要对时间进行比较时,需要先将字符串类型时间转换成日期类型,可使用convert函数进行转换,例如:

1
2
3
4
# 字符串转日期
select convert(date, '2005-12-31 23:59:59.9999999')
# 日期转字符串
select convert(varchar, getdate(), 1)

其中date是要转换的数据的类型,’2005-12-31 23:59:59.9999999’是被转换的数据。

日期比较

在mssql中比较两个日期的大小,可采用DATEDIFF函数,其语法如下:

1
DATEDIFF ( datepart , startdate , enddate )

其中,datepart是时间单位,startdate和enddate是合法的日期表达式。datediff用法示例如下:

1
2
3
4
5
6
7
8
9
10
11
SELECT DATEDIFF(year,        '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(quarter, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(month, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(dayofyear, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(day, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(week, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(hour, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(minute, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(second, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(millisecond, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');
SELECT DATEDIFF(microsecond, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000');

参考链接

  1. How to add hours to current date in SQL Server?,by stackoverflow.
  2. Choosing between DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo,by microsoft.
  3. datetime、datetime2的区别,by happymagic.
  4. DateTime , DateTime2 ,DateTimeOffset 之间的小区别,by 神崎橙.
  5. 关于“时间”的一次探索,by KohPoll.
  6. Date and Time Conversions Using SQL Server,by Edgewood Solutions.
  7. DATEDIFF (Transact-SQL),by microsoft.

JavaScript对象深度克隆

发表于 2019-10-10

JavaScript并没有提供对象的复制方法,只能借助第三方库或自己实现对象的深度克隆。具体方法如下:

第三方库实现

实现JavaScript库的深度克隆的第三方库主要有:

  • Underscore —— _.clone()
  • jQuery —— $.clone() / $.extend()
  • lodash —— _.clone() / _.cloneDeep()
  • 借助 JSON 全局对象

Underscore —— _.clone()

在 Underscore 中有这样一个方法:_.clone(),这个方法实际上是一种浅复制 (shallow-copy),所有嵌套的对象和数组都是直接复制引用而并没有进行深复制。其代码如下:

1
2
3
4
5
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

jQuery —— $.clone() / $.extend()

在 jQuery 中也有这么一个叫 $.clone() 的方法,可是它并不是用于一般的 JS 对象的深复制,而是用于 DOM 对象。与 Underscore 类似,可以通过 $.extend() 方法来完成深复制。值得庆幸的是,在 jQuery 中可以通过添加一个参数来实现递归extend。调用$.extend(true, {}, …)就可以实现深复制。

1
2
3
4
5
6
7
8
9
10
11
var x = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};

var y = $.extend({}, x), //shallow copy
z = $.extend(true, {}, x); //deep copy

y.b.f === x.b.f // true
z.b.f === x.b.f // false

lodash —— _.clone() / _.cloneDeep()

在lodash中关于复制的方法有两个,分别是.clone()和.cloneDeep()。其中.clone(obj, true)等价于.cloneDeep(obj)。

借助 JSON 全局对象

针对纯 JSON 数据对象的深复制,使用 JSON 全局对象的 parse 和 stringify 方法来实现深复制也算是一个简单讨巧的方法,但它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。

1
2
3
4
function jsonClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
var clone = jsonClone({ a: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
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;

// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}

// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, var len = obj.length; i < len; ++i) {
copy[i] = clone(obj[i]);
}
return copy;
}

// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

throw new Error("Unable to copy obj! Its type isn't supported.");
}

参考链接

  1. 深入剖析 JavaScript 的深复制,by 咀嚼之味.
  2. 深入理解JavaScript中的对象复制(Object Clone),by jingxian.
  3. JavaScript语言精粹(修订版),by Douglas Crockford著.

图片或多媒体大文件的存储与管理

发表于 2019-10-10 | 更新于 2019-10-11

图片、声音、视频等大文件在后台数据库中的存储管理方式通常有两种:

  • 文件存储到数据库中
  • 文件存储到文件系统中,数据库中保存文件路径

这两种文件存储管理方式有各自优缺点, 选择哪种方法存储大文件应根据具体情况而定。

文件系统存储管理方式

文件系统存储管理方式将文件存储到磁盘中,在数据库中使用varchar类型记录文件路径。该方法的缺点主要有:

  • 文件不支持DELETE

当删除文件路径时,不支持自动删除对应文件。

  • 文件不支持事务隔离

如果改变文件内容或删除文件,这些操作将立刻被其他客户端看到。

  • 文件不支持回滚

数据库记录能够恢复,但文件删除不能。

  • 文件不支持数据库的备份工具

  • 文件不支持SQL的访问权限设置

  • 文件不是SQL数据类型

数据库不会验证文件路径是否有效。当文件移动、重命名、删除时,数据不会自动更新路径。

数据库中存储文件

数据库通常支持BLOB类型,可用于存储任何二进制数据。数据库中存储文件,其优点对应文件系统管理存储方式的缺点。同样,数据库中存储文件也存在一些缺点:

  • 对数据库的读/写的速度永远都赶不上文件系统处理的速度
  • 数据库备份变的巨大,越来越耗时间
  • 对文件的访问需要穿越你的应用层和数据库层

多媒体大文件存储建议

当只涉及较小规模图片、音频的存储时,建议直接存放到数据库中。当涉及视频大文件存储时,建议还是用文件系统存储。

参考链接

  1. SQL反模式,by Bill karwin著, 谭振林, Push Chen译.
  2. 特殊格式文件(视频、声音等) 在数据库中的存储方式,by Jesson.
  3. 关于图片或者文件在数据库的存储方式归纳,by 王滔.
  4. SQL Server 2008新特性——FILESTREAM,by 曾毅.
  5. oracle对大对象类型操作:blob,clob,nclob,bfile,by lxy2330.
  6. 类似优酷土豆视频网站的视频源文件一般是存储在什么样系统上?,by zhihu.
  7. How to add hours to current date in SQL Server?,by stackoverflow.

Windows下QT与OSG开发环境配置

发表于 2019-10-10 | 更新于 2022-10-25

最近想尝试使用QT开发图形用户界面,OSG负责三维展示的解决方案,于是研究了一下Windows下OSG的编译安装配置,以及在QT中集成OSG的配置过程。过程记录如下:

QT+OSG开发环境配置

QT+OSG开发环境配置将使用QT Creator集成环境进行图形用户界面开发,使用Desktop Qt 5.9.8 MSVC2017 64构建套件编译链接调试C++程序。

Visual Studio Community 2017 下载安装

下载安装Visual Studio Community 2017的“使用C++的桌面开发”工作负载。这将为QT提供MSVC2017 64bit编译套件。

CDB 调试器下载安装

没有安装CDB调试器,QT的Desktop Qt 5.9.8 MSVC2017 64构建套件前会出现感叹号,并且使用该构建套件调试时会报“Unable to create a debugging engine”错误。CDB调试器下载安装方法参考QT - OSG 开发环境配置

QT 5.9.8 下载安装

QT 5.9.8是 LTS 版本,其下载安装请参考Qt5配置开源GSL数学库。需要注意的是,QT 5.9.8安装过程,选择MSVC2017 64bit编译组件,如图1所示。

QT 5.9.8安装选择MSVC2017 64bit编译组件

图1 QT 5.9.8安装选择MSVC2017 64bit编译组件

cmake下载安装

到 cmake 官网下载安装 cmake,用于osg的编译安装。

下载编译安装OSG

下载OSG及相关软件

Windows平台编译安装OSG需下载从OSG官网以下资料:

  • OpenSceneGraph 3.6.4源代码
  • OpenSceneGraph-Data-3.4.0,因为找不到3.6.4版本的数据
  • 3rdParty_VS2017_v141_x64_V11_full

配置OSG

打开cmake,选择osg源代码路径和osg源代码构建的路径,再点击“configure”按钮,选择Visual Studio 15 2017 Win64编译套件,配置osg如图2所示。

cmake编译osg配置

图2 cmake编译osg配置

需要配置的地方主要有:

  • ACTUAL_3rdparty_DIR:配置第三方库的目录
  • Build_OSG_EXAMPLES:选中即编译安装OSG的示例。
  • CMAKE_INSTALL_PREFIX:设置OSG的安装目录,通常放到C盘以外的地方,防止因权限问题安装失败。

配置完成后,再持续点击“configure”按钮,知道红色警告消失。然后点击“Generate”按钮生成vs2017工程。

编译OSG

在cmake中点击“Open Project”按钮,即使用 VS2017 打开 build 文件夹下的工程,点击:生成 -> 批生成 -> 生成 Debug 和 Release 版本的 All_BUILD 即可。

安装OSG

右击解决方案的 Install 项目,点击生成,然后 Debug 平台的库文件就开始安装了,同理选择 Release 平台再重复安装。之后就可以在安装路径中看到编译的库文件了,安装路径由变量CMAKE_INSTALL_PREFIX的配置决定。

配置环境变量

OSG官方文档 Windows Compiling with Visual Studio指出,OSG安装完成后需要配置如下环境变量:

1
2
3
4
5
6
7
8
OSG_ROOT points to the base of the OSG file structure (the directory that contains include, src etc. subdirectories)
OSG_BIN_PATH = %OSG_ROOT%\bin
OSG_INCLUDE_PATH = %OSG_ROOT%\include
OSG_LIB_PATH = %OSG_ROOT%\lib
OSG_SAMPLES_PATH = %OSG_ROOT%\share\OpenSceneGraph\bin
OSG_FILE_PATH = ???\OpenSceneGraph-Data-X.X
OPENSCENEGRAPH_VERSION = 3.6.4
PATH 添加:%OSG_ROOT%\bin;%OSG_ROOT%\bin\osgPlugins-%OPENSCENEGRAPH_VERSION%; 这两个是配置 bin 和插件的路径。

OSG测试

命令行测试

打开 cmd 命令行,输入osgversion,输出当前 osg 版本说明环境变量配置成功了,再来看看能不能导入模型文件,接着键入:

1
osgviewer cow.osg

这里可能会提示系统缺少 zlibd.dll,解决方法很简单:

  • 拷贝第三方库的 dll 目录下的 zlib.dll 和 zlibd.dll 到 OSG 安装目录的 bin 目录中。
  • 拷贝第三方库的 lib 目录下的 zlib.dll 和 zlibd.dll 到 OSG 安装目录的 lib 目录中。

这时重新键入上面的命令,就可以看到一头经典的 3D 牛了,说明 OSG 安装成功了。

QT 配置 OSG 库路径

新建一个 QT 的控制台项目,编辑 .pro 文件,加入 OSG 库的路径:

1
2
3
4
5
6
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../OpenSceneGraph/lib/ -lOpenThreads -losg -losgDB -losgUtil -losgGA  -losgViewer -losgText
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../OpenSceneGraph/lib/ -lOpenThreadsd -losgd -losgDBd -losgUtild -losgGAd -losgViewerd -losgTextd
else:unix: LIBS += -L$$PWD/../../../OpenSceneGraph/lib/ -lOpenThreads -losg -losgDB -losgUtil -losgGA -losgViewer -losgText

INCLUDEPATH += $$PWD/../../../OpenSceneGraph/include
DEPENDPATH += $$PWD/../../../OpenSceneGraph/lib

main.cpp 如下:

1
2
3
4
5
6
7
8
9
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>

int main(int argc, char *argv[])
{
osgViewer::Viewer viewer;
viewer.setSceneData(osgDB::readNodeFile("cow.osg"));
return viewer.run();
}

运行,出来一头 3D 牛,即配置成功。

参考链接

  1. QT - OSG 开发环境配置,by 程序小哥.
  2. Qt5配置开源GSL数学库,by jackhuang.
  3. Ubuntu16.04编译安装OSG,by jackhuang.
  4. Windows Compiling with Visual Studio,by OpenSceneGraph.
  5. Visual_Studio_Community_2017离线安装,by jackhuang.
  6. Qt-Creator常见问题,by XenonSec.
  7. Visual Studio: MSB3073 error exited with code 1,by stackoverflow.
  8. Qt5.7 + VS2015 环境搭建,by 一去丶二三里.
  9. OSG学习:WIN10系统下OSG+VS2017编译及运行,by 路人甲JIA.
  10. error LNK2019: 无法解析的外部符号 “__declspec(dllimport) public,by OSG_weiguoying.
  11. 应用篇之dll lib pdb和头文件,by 阿兵-AI医疗.
  12. GL.h文件提示报错,by Parkergh.
  13. #undef 的用法及其意义,by DinnerHowe.

Windows忘记密码无法登录的解决方案

发表于 2019-10-09 | 更新于 2019-10-10

突然需要在一台很久不用的Win7计算机上差一点资料,可忘记了登录密码,只能重置密码。过程记录如下:

解决思路

借助手头的一张linux live cd,从光盘启动,进入linux系统,然后找到windows系统盘分区,在C:\Windows\System32下,将cmd.exe重命名为Magnify.exe,然后系统重启,登录Windows操作系统,在登录界面中,调用辅助工具放大镜,即打开命令提示符,输入如下命令重置Windows账户密码:

1
2
3
4
5
6
// 查看Windows账户
net user
// 修改Windows账户密码
net user Administrator 123456
// 重启系统用Administrator账户登录
shutdown -r -t 00

或者采用如下命令新建登录账户:

1
2
3
4
5
6
// 创建用户
net user test 123456 /add
// 用户提权,加入管理员组
net localgroup Administrators test /add
// 使用完删除用户
net user test /delete

参考链接

  1. 关闭windows默认共享,by Blue_sky90.

Qt5配置开源GSL数学库

发表于 2019-10-07 | 更新于 2021-04-28

GNU科学库(英语:GSL - GNU Scientific Library),是一套为 C语言和 C++开发者提供的数值计算函数库,多用于应用数学和科学。软件属于自由软件,在 GNU 通用公共许可证下发布。

这套数值计算函数库提供了大量的数学运算功能,比如随机数生成器,特殊函数以及最小二乘拟合。有超过1000个函数包含在该数值计算库和扩展的测试包中。

下面介绍在Qt5环境中搭载GSL库的方法和步骤。

下载并安装Qt5

下载Qt5请参考Qt下载(多种下载通道+所有版本)。将下载并安装Qt5.9.8 LTS。

安装Qt5请参考图解Qt安装(Windows平台)。

环境配置

MinGW环境配置

MinGW(Minimalist GNU for Windows),又称mingw32,是将GCC编译器和GNU Binutils移植到Win32平台下的产物,包括一系列头文件(Win32API)、库和可执行文件。

另有可用于产生32位及64位Windows可执行文件的MinGW-w64项目,是从原本MinGW产生的分支。如今已经独立发展。

MinGW是从Cygwin(1.3.3版)基础上发展而来。Qt5.9.8使用MinGW 5.3编译器编译程序。

将mingw的bin目录添加进系统的环境变量,告诉系统已经安装了mingw编译器,并且可以被使用。Qt5自带的mingw的安装路径为C:\Qt\Qt5.9.8\5.9.8\mingw53_32,这里我们需要将C:\Qt\Qt5.9.8\5.9.8\mingw53_32\bin目录添加进入系统环境变量。

系统环境变量的设置方法为:【计算机】->【属性】->【高级系统设置】->【环境变量】->【系统变量】->【Path】。

MSYS环境配置

MSYS是bash,make,gawk和grep等GNU实用程序的集合,以允许构建依赖于传统UNIX工具的应用程序和程序。 它旨在补充MinGW和cmd shell的缺陷。

一个示例是构建一个使用自动工具构建系统的库。 用户通常会先运行“ ./configure”,然后运行“ make”来构建它。 配置外壳程序脚本需要Windows系统上不存在的外壳程序脚本解释器,但由MSYS提供。

一个常见的误解是MSYS是“ Windows上的UNIX”,MSYS本身不包含编译器或C库,因此不能将UNIX程序神奇地移植到Windows上,也不能提供任何特定于UNIX的功能,例如case- 敏感文件名。 寻找此类功能的用户应改用Cygwin或Microsoft的Interix。

msys的下载网址为:

msys+7za+wget+svn+git+mercurial+cvs-rev13.7z

上述网址给出的是一个压缩包,直接解压就可以使用。

将msys+7za+wget+svn+git+mercurial+cvs-rev13.7z解压到C:\Qt\Qt5.9.8\5.9.8\mingw53_32。

仅将msys解压后并不能直接使用,因为对于msys而言,它还不知道mingw编译器所处位置,因此需要对msys的环境进行配置。首先需要在msys的路径下创建一个名为mingw的空文件夹,路径为C:\Qt\Qt5.9.8\5.9.8\mingw53_32\msys\mingw。然后修改
C:\Qt\Qt5.9.8\5.9.8\mingw53_32\msys\etc\fstab中的内容,如下所示。

1
C:/Qt/Qt5.9.8/Tools/mingw530_32 /mingw

通过上述环境配置,当启动msys时,将Qt5的mingw编译直接挂载到/mingw目录下。

双击msys文件夹下msys.bat即可运行msys,使用cd命令进入/mingw目录下,我们就会发现此目录下文件就是C:\Qt\Qt5.9.8\Tools\mingw530_32对应的文件。

跳转到gsl-2.6文件夹,即可开始编译链接gsl。

下载并编译安装GSL

从此处 http://ftpmirror.gnu.org/gsl/ 下载最新的GSL-2.6。然后采用如下步骤编译GSL:

  1. 运行msys,进入GSL源文件所在目录
  2. ./configure
  3. make -j8
  4. make install

Qt调用GSL库

创建Qt工程

为了测试GSL数学库能够在Qt环境中运行,新建一个控制台工程进行测试。

新建控制台应用程序的步骤为:【Welcome】->【New Project】->【Application】->【Qt Console Application】->【Choose】->【将工程命名为GslTest】。

引用GSL库

在GslTest项目的工程目录内,新建名为gsl的文件夹。该文件夹的内容如图1所示。

gsl文件夹结构

图1 gsl文件夹结构

gsl文件夹下lib、include、bin中内容为编译安装GSL过程中生成的头文件、库文件及生成的相关exe文件,主要从C:\Qt\Qt5.9.8\5.9.8\mingw53_32\msys路径下提取。

此外,在gsl目录下新建gsl.pro文件,其内容如下:

1
2
3
4
5
# 宏定义项,指明采用GSL,因为有些函数需要使用dll
DEFINES += GSL_DLL
INCLUDEPATH += $$PWD\include
LIBS += -L$$PWD\lib -llibgsl
LIBS += -L$$PWD\lib -llibgslcblas

然后在GslTest的项目文件GslTest.pro末尾引用gsl.pro文件,如下所示。

1
include($$PWD/gsl/gsl.pro)

修改GslTest的main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QCoreApplication>
#include <gsl/gsl_sf_bessel.h>
#include <math.h>
#include <iostream>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
double x=10.0;
double y=gsl_sf_bessel_J0(x);
std::cout<<"J0("<<x<<")="<<y<<std::endl;

return a.exec();
}

运行GslTest

GslTest运行结果

图2 GslTest运行结果

参考链接

  1. Qt下载(多种下载通道+所有版本),by C语言中文网.
  2. 图解Qt安装(Windows平台),by C语言中文网.
  3. GSL - GNU Scientific Library,by gnu.
  4. QT5环境下配置GSL数学库,by 木小猿.
  5. GNU科学库,by wikipedia.
  6. 【ICPC-455】C++ 常用数学函数库,by 陈国林.
  7. MinGW,by wikipedia.
  8. MinGW和MSYS区别和关系以及MinGW&MSYS在Win7中安装并编译x264,by FreeApe.
  9. 最强数学库GSL(GNU Scientific Library) Qt环境下部署,by 尘中远.
  10. Qt5.9.6使用MSVC(VS2017)开发环境搭建,by KirkSong.
  11. QT - OSG 开发环境配置,by 程序小哥.
  12. GSL with CMake build support,by ampl.
  13. 在Visual Studio中使用GNU Scientific Library (GSL),by NULL_BOT.
上一页1…333435…53下一页

Jack Huang

523 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse