目录

第1章简介    1

1.1 DFB    1

1.2 DDB    1

1.3 DIB    2

第2章相关API    3

2.1 创建    3

2.1.1 CreateCompatibleBitmap    3

2.1.2 CreateBitmap    3

2.1.3 CreateBitmapIndirect    4

2.1.4 CreateDIBitmap    4

2.1.5 CreateDIBSection    5

2.1.6 小结    6

2.2 查询    6

2.3 载入DIB    6

2.4 访问位图数据    7

2.5 位图传输    7

第3章示例代码    8

3.1 屏幕拷贝    8

3.1.1 CaptureScreen    10

3.1.2 BmpToClipboard    11

3.1.3 BmpToFile    11

3.2 读取位图文件并显示    12

第1章简介

下图显示了三种位图:DFB、DDB、DIB。

图1.1

1.1 DFB

DFB(Device-format bitmap)是设备格式位图。假如设备是显示器,那么DFB就在显卡内存中。DFB格式并没有统一的标准,所以直接访问DFB并不是一个明智的做法。事实上,在Windows系统里一般是无法直接访问DFB的,程序员常常使用设备的HDC与之联系,如下面的三行代码将在屏幕上绘制一个矩形,其实质就是通过屏幕的HDC访问DFB,往显存里绘制了一个矩形。

HDC hDC = GetDC(NULL);            //获得屏幕的 HDC

Rectangle(hDC,200,100,50,90);            //绘制一个矩形

ReleaseDC(NULL,hDC);                    //释放 HDC

1.2 DDB

DDB(Device-dependent bitmap)是设备相关位图,其实就是HBITMAP。笔者认为它存在的理由之一就是:通过HDC访问DFB的效率有时太低了。它叫设备相关位图是因为它的内部格式与DFB是有关系的,保证了与DFB相互转换、传输时的高效率。

访问DDB的方法有三种:

1、通过内存HDC;

2、分配内存,通过GetDIBits获取位图数据,通过SetDIBits将修改后的位图数据写入HBITMAP;

3、创建DDB时获取位图数据的首地址,这样就可以直接访问位图了。不过这种方法创建的位图实质上已经不是DDB了,它不再与DFB有关联,相互转换时不能保证最高的效率。

DDB与DFB可通过BitBlt等函数相互转换、传输。

1.3 DIB

DDB的格式随DFB的格式而变,如果需要根据不同的DDB格式编写不同的代码去操纵一个像素,那将是程序员的恶梦。这个时候就需要DIB了。

DIB(Device-independent bitmap)是设备无关位图,是程序员访问DDB时的位图标准格式。知道了这个格式就可以对位图中的单个像素进行高效的操作了。DIB位图读写完毕后,可通过API函数将其转换为DDB。

DIB的实质就是一种位图格式,而且它基本上就是.bmp文件格式。所以,把DIB理解为加载进内存中的.bmp文件也是可以的。

DIB通过GetDIBits、SetDIBits访问DDB。

DIB通过SetDIBitsToDevice等函数可直接访问DFB。因为格式不同,所以需要大量的转换,不能保证高效性。

第2章相关API

2.1 创建

创建位图的API有5个:

CreateBitmap

CreateBitmapIndirect

CreateCompatibleBitmap

CreateDIBitmap

CreateDIBSection

下面逐个介绍

2.1.1 CreateCompatibleBitmap

使用示例:

HDC        hDC    =    GetDC(NULL);

HBITMAP    hBmp    =    CreateCompatibleBitmap(hDC,100,50);

第一行代码获取屏幕的 hDC;

第二行代码将创建与屏幕兼容的DDB位图,其大小为100×50。

hBmp与hDC兼容的含义就是其格式尽量与hDC的DFB保持一致,这样在与DFB相互转换、传输时效率就会比较高。

2.1.2 CreateBitmap

此函数的声明如下:

HBITMAP CreateBitmap(int nWidth,                //位图宽,单位:像素

int nHeight,                    //位图高,单位:像素

UINT cPlanes,                //位图颜色面数

UINT cBitsPerPel,            //一个像素的比特数

CONST VOID *lpvBits);    //对像素进行初始化

它的实质就是创建了一个三维数组。数组大小为:nWidth×nHeight×cPlanes,数组中的每个元素代表一个像素,每个像素占用的比特数为cBitsPerPel。

如果要对三维数组进行初始化,请指定参数lpvBits。

现在再来看看上一节的代码:

HBITMAP    hBmp    =    CreateCompatibleBitmap(hDC,100,50);

它可以被CreateBitmap函数代替,具体代码如下:

HBITMAP    hBmp    =    CreateBitmap(100,50

,GetDeviceCaps(hDC,PLANES)

,GetDeviceCaps(hDC,BITSPIXEL)

,NULL);

2.1.3 CreateBitmapIndirect

CreateBitmapIndirect是CreateBitmap的简化版本,如下面的代码:

BITMAP bm;

CreateBitmapIndirect(&bm);

等价于下面的代码:

CreateBitmap(bm.bmWidth,bm.bmHeight

,bm.bmPlanes,bm.bmBitsPixel,bm.bmBits);

2.1.4 CreateDIBitmap

CreateDIBitmap不是创建一个DIB位图,而是创建一个DDB位图,然后用DIB位图数据对这个DDB位图进行初始化。其声明如下:

HBITMAP CreateDIBitmap(HDC hdc,

CONST BITMAPINFOHEADER *lpbmih,    //指定位图的宽和高

DWORD fdwInit,                            //是否初始化

CONST VOID *lpbInit,                        //DIB像素数据首地址

CONST BITMAPINFO *lpbmi,                //DIB信息及颜色表)

UINT fuUsage);                                //DIB_RGB_COLORS

它等效于如下代码,即首先创建DDB位图,然后再根据DIB位图数据对其进行初始化。

HBITMAP hBmp = CreateCompatibleBitmap(hDC

,lpbmi->biWidth,labs(lpbmi->biHeight));

if(CBM_INIT==fdwInit)

{

SetDIBits(hDC,hBmp,0,labs(lpbmi->biHeight),lpbInit,lpbmi,fuUsage);

}

2.1.5 CreateDIBSection

以上创建的位图都是DDB位图,CreateDIBSection创建的将是一个DIB位图,其声明如下:

HBITMAP CreateDIBSection(

HDC hdc, // handle to device context

CONST BITMAPINFO *pbmi,

// pointer to structure containing bitmap size,

// format, and color data

UINT iUsage, // color data type indicator: RGB values or

// palette indexes

VOID **ppvBits, // pointer to variable to receive a pointer to

// the bitmap's bit values

HANDLE hSection, // optional handle to a file mapping object

DWORD dwOffset // offset to the bitmap bit values within the

// file mapping object

);

第1个参数hdc一般没什么用,仅当iUsage为DIB_PAL_COLORS时才会用到它;

第2个参数pbmi指定了DIB位图的宽度、高度、颜色位及颜色表;

第3个参数iUsage说明了颜色表中的颜色含义。iUsage为DIB_RGB_COLORS时,颜色表中的颜色是RGB格式;iUsage为DIB_PAL_COLORS时,颜色表中的颜色是索引值,这个时候就需要用第1个参数hdc将索引值转换为RGB值;

第4个参数ppvBits是一个输出参数,*ppvBits是一个void*,它指向了DIB像素数据的首地址。也就是说CreateDIBSection会为DIB位图分配内存,然后把像素数据首地址通过ppvBits传出来,程序员根据此地址即可对DIB位图进行操作。

最后两个参数一般不用,直接设置为NULL和0即可。

2.1.6 小结

CreateCompatibleBitmap是最简单且经常用到的;

CreateBitmap是最正规的;

CreateBitmapIndirect是CreateBitmap的简化版;

CreateDIBitmap在创建DDB位图后,用DIB位图进行初始化;

CreateDIBSection最特殊,它创建的不是DDB,而是DIB。也只有它能获得位图数据的地址,能够对位图数据进行直接操作。

2.2 查询

给定一个HBITMAP hBmp,如何得知它的宽度、高度等信息?答案就是使用GetObject函数,其示例代码如下:

BITMAP bm;

GetObject(hBmp,sizeof(bm),&bm);

此时:

bm.bmWidth;         //位图宽度,单位:像素

bm.bmHeight;         //位图高度,单位:像素。如果是DIB,它可能为负

bm.bmWidthBytes;     //位图一行的字节数

bm.bmPlanes;         //位图颜色面数

bm.bmBitsPixel;     //每个像素的比特数

bm.bmBits;            //如果是DIB,它就是位图数据的地址,否则为NULL

只有通过CreateDIBSection获得的HBITMAP,它对应的bm.bmBits才不为NULL。此时,下面的代码将获得更加详细的信息:

DIBSECTION ds;

GetObject(hBmp,sizeof(ds),&ds);

DIBSECTION 的定义请参考 MSDN。

2.3 载入DIB

LoadBitmap        从资源里载入位图

LoadImage        从资源或文件里载入位图

2.4 访问位图数据

GetBitmapBits和SetBitmapBits可用于访问位图中的像素数据。这两个函数不能获得颜色表,属于过时的函数。

GetDIBits和SetDIBits是GetBitmapBits和SetBitmapBits的升级版本,它们增加了处理颜色表的功能。

2.5 位图传输

位图传输主要有如下几个函数

BitBlt

MaskBlt

PlgBlt

StretchBlt

TransparentBlt

上面的函数StretchBlt,可以用SetStretchBltMode设置模式,用GetStretchBltMode获取模式。

上面几个函数都是在两个HDC之间传输位图,下面两个函数是将DIB位图直接传输到HDC上:

SetDIBitsToDevice

StretchDIBits

第3章示例代码

3.1 屏幕拷贝

Windows系统里,按下 Print Screen 键即可将屏幕位图拷贝到剪贴板中。下面是实现此功能的 MFC 代码:

HBITMAP CaptureScreen()

{

int    cx            =    GetSystemMetrics(SM_CXSCREEN);

int    cy            =    GetSystemMetrics(SM_CYSCREEN);

HDC        hDC        =    GetDC(NULL);

HBITMAP    hBmp        =    CreateCompatibleBitmap(hDC,cx,cy);

HDC        hMem        =    CreateCompatibleDC(hDC);

HGDIOBJ    hBmpOld    =    SelectObject(hMem,hBmp);

BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY);

SelectObject(hMem,hBmpOld);

DeleteDC(hMem);

ReleaseDC(NULL,hDC);

return hBmp;

}

void BmpToClipboard(HBITMAP hBmp)

{

if(OpenClipboard(NULL))

{

EmptyClipboard();

SetClipboardData(CF_BITMAP,hBmp);

CloseClipboard();

}

}

void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile)

{

BITMAP bm;

GetObject(hBmp,sizeof(bm),&bm);

if(0 == wBitsPixel)

{

wBitsPixel = bm.bmBitsPixel;

}

DWORD dwSizeClr = 0; //颜色表字节数

if(wBitsPixel <= 8)

{

dwSizeClr = (1 << wBitsPixel) * sizeof(RGBQUAD);

}

//一行的字节数

DWORD dwLineBytes = ((bm.bmWidth * wBitsPixel + 31) & ~31) >> 3;

//像素数据字节数

DWORD dwSizeImg = dwLineBytes * bm.bmHeight;

BITMAPFILEHEADER bmfh;

bmfh.bfType = 0x4D42; //BM

bmfh.bfOffBits = sizeof(bmfh) + sizeof(BITMAPINFOHEADER) + dwSizeClr;

bmfh.bfSize = bmfh.bfOffBits + dwSizeImg;

bmfh.bfReserved1 = 0;

bmfh.bfReserved2 = 0;

BYTE* pBmpFile = (BYTE*)malloc(bmfh.bfSize);

memcpy(pBmpFile,&bmfh,sizeof(bmfh));

LPBITMAPINFO lpbi = (LPBITMAPINFO)(pBmpFile + sizeof(bmfh));

lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

lpbi->bmiHeader.biWidth = bm.bmWidth;

lpbi->bmiHeader.biHeight = bm.bmHeight;

lpbi->bmiHeader.biPlanes = 1;

lpbi->bmiHeader.biBitCount = wBitsPixel;

lpbi->bmiHeader.biCompression = BI_RGB;

lpbi->bmiHeader.biSizeImage = dwSizeImg;

lpbi->bmiHeader.biXPelsPerMeter = 0;

lpbi->bmiHeader.biYPelsPerMeter = 0;

lpbi->bmiHeader.biClrUsed = 0;

lpbi->bmiHeader.biClrImportant = 0;

HDC hDC = GetDC(NULL);

GetDIBits(hDC,hBmp,0,bm.bmHeight

,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS);

ReleaseDC(NULL,hDC);

CFile f;

f.Open(szFile,CFile::modeWrite | CFile::modeCreate);

f.Write(pBmpFile,bmfh.bfSize);

f.Close();

free(pBmpFile);

}

void CTestDlg::OnButton3()

{

HBITMAP hBmp = CaptureScreen();

if(hBmp)

{

BmpToClipboard(hBmp);

BmpToFile(hBmp, 0,_T("0.bmp"));

BmpToFile(hBmp, 1,_T("1.bmp"));

BmpToFile(hBmp, 4,_T("4.bmp"));

BmpToFile(hBmp, 8,_T("8.bmp"));

BmpToFile(hBmp,16,_T("16.bmp"));

BmpToFile(hBmp,24,_T("24.bmp"));

BmpToFile(hBmp,32,_T("32.bmp"));

DeleteObject(hBmp);

}

}

当用户单击Button3时,会调用CTestDlg::OnButton3。它做了三项工作:

1、调用CaptureScreen(),将屏幕的DFB转换为DDB;

2、调用BmpToClipboard(hBmp);将DDB复制到剪贴板里;

3、调用BmpToFile(hBmp, 0,_T("0.bmp"));将DDB保存到文件里。

下面逐个进行介绍。

3.1.1 CaptureScreen

屏幕拷贝的实质就是将显存里的DFB提取出来,转换为DDB。

根据图1.1可知,DFB与DDB可通过函数BitBlt相互转换、传输。

现在的问题是:BitBlt只能在两个HDC之间相互传输位图,并不能直接操纵HBITMAP。这里就需要内存HDC。

CaptureScreen的详细讲解如下:

//下面两行代码用来获得屏幕的宽度和高度,单位是像素。

int cx = GetSystemMetrics(SM_CXSCREEN);

int cy = GetSystemMetrics(SM_CYSCREEN);

//下面这行代码用来获得屏幕的HDC

HDC hDC = GetDC(NULL);

//创建与屏幕兼容的DDB

HBITMAP hBmp = CreateCompatibleBitmap(hDC,cx,cy);

//创建与屏幕兼容的内存HDC

HDC hMem = CreateCompatibleDC(hDC);

//把DDB选入内存HDC

HGDIOBJ hBmpOld = SelectObject(hMem,hBmp);

//将DFB传输给DDB

BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY);

//恢复内存HDC原来的位图

SelectObject(hMem,hBmpOld);

//销毁内存HDC

DeleteDC(hMem);

//释放屏幕HDC

ReleaseDC(NULL,hDC);

3.1.2 BmpToClipboard

BmpToClipboard函数把DDB复制到系统剪贴板内,代码很简单,不用多解释。

3.1.3 BmpToFile

BmpToFile函数用来把DDB转换为DIB,然后把DIB存入文件里。它的参数如下所示:

void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile)

第一个参数是DDB;

第二个参数是转换为DIB时的颜色位数。0 表示采用DDB的颜色位数,其它的取值有:1、2、4、8、16、24、32。颜色位数越大,颜色数就越多,生成的文件越大;

第三个参数是保存时的文件名。

BmpToFile函数的关键点有:

1、调用GetDIBits,把DDB的位图数据以DIB的格式提取出来;

2、调用f.Write(pBmpFile,bmfh.bfSize);把DIB数据写入文件。下面就是.bmp文件的格式:

图3.1

首先是一个BITMAPFILEHEADER。pBmpFile指向它;

接着是一个BITMAPINFOHEADER。lpbi指向它;BITMAPINFO的第一个成员变量就是BITMAPINFOHEADER型的,所以也可以把lpbi看做BITMAPINFO*;

对于颜色位数小于16的DIB需要定义颜色表,即定义多个RGBQUAD。对于颜色位数大于等于16的DIB,一般是不需要RGBQUAD的。

lpbi->bmiColors指向颜色表。颜色表的项数为(1 << wBitsPixel),颜色表的字节数为(1 << wBitsPixel) * sizeof(RGBQUAD);

中间还有一个"未定义",它可以是零字节也可以是多个字节,内容随便填。

最后是像素数据,pBmpFile + bmfh.bfOffBits指向它。

现在再看看GetDIBits的代码:

GetDIBits(hDC,hBmp,0,bm.bmHeight

,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS);

第5个参数pBmpFile + bmfh.bfOffBits指向像素数据;

第6个参数lpbi是一个BITMAPINFO*。lpbi->bmiColors指向颜色表。

调用GetDIBits将改写颜色表(lpbi->bmiColors)和像素数据(pBmpFile + bmfh.bfOffBits)。

3.2 读取位图文件并显示

读取位图文件并显示的代码有两份。

第一份代码如下所示:

void CTestDlg::OnButton4()

{

HBITMAP hBmp = (HBITMAP)LoadImage(NULL,_T("0.bmp")

,IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);

if(hBmp)

{

BITMAP bm;

GetObject(hBmp,sizeof(bm),&bm);

CClientDC dc(this);

HDC hMem = CreateCompatibleDC(dc.m_hDC);

HGDIOBJ hBmpOld = SelectObject(hMem,hBmp);

::BitBlt(dc.m_hDC,0,0,bm.bmWidth,bm.bmHeight,hMem,0,0,SRCCOPY);

SelectObject(hMem,hBmpOld);

DeleteDC(hMem);

DeleteObject(hBmp);

}

}

说明:

1、载入位图使用了LoadImage函数。

2、显示位图的步骤:

1)创建一个与屏幕兼容的内存HDC;

2)将载入的位图选入内存HDC;

3)调用BitBlt将内存HDC的内容传输到屏幕HDC上;

4)销毁内存HDC。

第二份代码如下所示:

void CTestDlg::OnButton5()

{

CFile f;

if(f.Open(_T("1.bmp"),CFile::modeRead))

{

DWORD dwSize = f.GetLength();

if(dwSize > sizeof(BITMAPFILEHEADER)

+ sizeof(BITMAPINFOHEADER))

{

BYTE* pBmpFile = (BYTE*)malloc(dwSize);

f.Read(pBmpFile,dwSize);

BITMAPFILEHEADER*pbmfh =

(BITMAPFILEHEADER*)pBmpFile;

if(pbmfh->bfType == 0x4D42)

{

BITMAPINFOHEADER*pbmih =

(BITMAPINFOHEADER*)(pBmpFile + sizeof(BITMAPFILEHEADER));

CClientDC dc(this);

SetDIBitsToDevice(dc.m_hDC

,0,0,pbmih->biWidth,pbmih->biHeight

,0,0,0,pbmih->biHeight

,pBmpFile + pbmfh->bfOffBits

,(BITMAPINFO*)pbmih

,DIB_RGB_COLORS);

}

free(pBmpFile);

}

f.Close();

}

}

说明:

1、载入位图就是打开位图文件,然后将所有内容载入到内存;

2、显示位图没有使用BitBlt,而是使用SetDIBitsToDevice直接把DIB传输到屏幕。其中pBmpFile + pbmfh->bfOffBits指向像素数据,(BITMAPINFO*)pbmih指向BITMAPINFO,pbmih->bmiColors指向颜色表。

Windows 位图的更多相关文章

  1. Windows API 的数据类型与 Delphi 数据类型对照表

    Windows 数据类型 Delphi 数据类型 描述 LPSTR PAnsiChar 字符串指针 LPCSTR PAnsiChar 字符串指针 DWORD LongWord 整数 BOOL Long ...

  2. 【转载】Windows api数据类型

    最近在接触windows api函数,看到了很多之前没有看到过的数据类型,发现“个人图书馆”中有个帖子说的挺详细的,特地搬运过来 Windows 数据类型 Delphi 数据类型 描述 LPSTR P ...

  3. python中提取位图信息(AttributeError: module 'struct' has no attribute 'unstack')

    前言 今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用.和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读. 0x00 ...

  4. windows的各种扩展名详解

    Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...

  5. CreateDIBSection和位图结构

    2019独角兽企业重金招聘Python工程师标准>>> 理解分辨率 我们常说的屏幕分辨率为640×480,刷新频率为70Hz,意思是说每行要扫描640个象素,一共有480行,每秒重复 ...

  6. 学习 opencv---(2) 图像的载入,显示和输出

    了解过之前老版本OpenCV的童鞋们都应该清楚,对于OpenCV1.0时代的基于 C 语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会照成内存泄露.而且用起来超 ...

  7. OpenCV阶段总结扩充。

    Mat类型简单介绍 /* cv::Mat类是用于保存图像以及其他矩阵的数据结构.默认情况下,其尺寸为0,我们也可以设置其初始尺寸,比如定义一个Mat类的对象,就要写cv::Mat pic(320,64 ...

  8. BMP图像数据格式详解

    一.简介 BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式.Windows系统内部各图像绘制操作都是以B ...

  9. cvLoadImage

    编辑 本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧! 函数原型:IplImage* cvLoadImage( const char* filename, int flags= ...

随机推荐

  1. CentOS 6.3下源码安装LAMP(Linux+Apache+Mysql+Php)环境

    一.简介 什么是LAMP    LAMP是一种Web网络应用和开发环境,是Linux, Apache, MySQL, Php/Perl的缩写,每一个字母代表了一个组件,每个组件就其本身而言都是在它所代 ...

  2. javascript耐人寻味

    在思考javascript解释过程的时候,看过别人几篇文章,自己做了几个测试 容易理解,在javascript,形如这样的代码可以正常执行: alert(hello()); function hell ...

  3. 一、Java语言基础

    1.标识符和关键字 标识符是java中必须使用的,具有一定的规则,用来标识类名.变量名.方法名.数组名.文件名等. 例:int i = 100;                    变量i就是标识符 ...

  4. Spring MVC 详解(二)

    前端控制器 在web.xml中配置: 在springmvc.xml中配置springmvc架构三大组件(处理器映射器.适配器.视图解析器) 处理器映射器 在springmvc.xml中配置: Bean ...

  5. HDU 5046 Airport(dlx)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5046 题意:n个城市修建m个机场,使得每个城市到最近进场的最大值最小. 思路:二分+dlx搜索判定. ...

  6. Android 网格布局短信发送界面

    <?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android=" ...

  7. Python的getattr(),setattr(),delattr(),hasattr()

    判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False.需要注意的是name要用括号括起来   1 >>> class ...

  8. STORM_0009_Lifecycle-of-a-topology/拓扑的生命周期

    http://storm.apache.org/releases/1.0.1/Lifecycle-of-a-topology.html STORM拓扑的生命周期   本页的内容基于0.7.1代码,后来 ...

  9. thinkphp 初始配置

    他喵的,去做了个其他的模板,一段时间不碰tp,居然配置了好久 记录留备用 一.把下好的ThinkPHP放到根目录的文件夹下 ,例如www文件夹下 在www目录下新建文件夹admin和home 新建入口 ...

  10. Redis高级实践之————Redis短连接性能优化

    摘要: 对于Redis服务,通常我们推荐用户使用长连接来访问Redis,但是由于某些用户在连接池失效的时候还是会建立大量的短连接或者用户由于客户端限制还是只能使用短连接来访问Redis,而原生的Red ...