网络协议中常见编码的原理及其解码函数的C++实现

网络协议分析中常见编码的原理及其解码

基于C++语言实现

本文由CSDN-蚍蜉撼青松 【主页:http://blog.csdn.net/howeverpf】原创,转载请注明出处!

 

一、Base64解码

1.1 简介与编码原理

       在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。完整的base64定义可见RFC 1421和RFC 2045。

----------------------------本段引自*http://zh.wikipedia.org/wiki/Base64

       Base64编码的过程是把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后对于每个6Bit,在其高位再添两位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。由此可以估算编码后数据长度大约为原长的135.1%。

Base64编码的规则

  1. 对输入的原始字节序列,按每3个8bit的字节分为一组,每组共24bit;【三三分组
  2. 针对每组分别处理,将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节;【四变三
  3. 将这4个8bit的字节分别转换成10进制,对照Base64编码表 (见图1-1),得到对应编码后的可打印字符;【查表
  4. 每76个字符加一个换行符;【换行处理
  5. 当输入的原始字节序列长度不是3的整数倍时,需在输入字节序列最后用零补齐。在这种情况下,编码后结果必然不足四个字节,需在编码结果最后用‘=‘补齐;这就保证编码后字节序列的长度一定是4的倍数。【结束处理

网络协议中常见编码的原理及其解码函数的C++实现

图1-1 Base64编码表(截自*

 

1.2 Base64解码函数

/***********************************************************/
/*                       输入参数说明:                      */
/*               src为指向存放输入内容的缓冲区指针             */
/*                       输出参数说明:                      */
/*               返回值为指向存放输出内容的缓冲区指针           */
/***********************************************************/
char * Base64_Decode(char *src)
{
    int n,i,j;
    int equal_count=0;//等号的个数
    char *p=NULL;
    char *dst=NULL;
    char  ch64[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    n=strlen(src);
    if (n%4!=0)//输入不对 返回空指针
    {
        debug(stderr,"Base64_Decode input error");
        return dst;
    }
    if (*(src + n - 1) == ‘=‘)
    {
        equal_count += 1;
    }
    if (*(src + n - 2) == ‘=‘)
    {
        equal_count += 1;
    }
    for (i=0;i<n;i++)
    {
        p=strchr(ch64,src[i]);//p指向src[i]在ch64中的位置
        if (!p)               //没找到,为"=",不转换
            break;
        src[i]=p-ch64;//将src[i]转换为0-63的数字
    }
    dst=new char[n*3/4+1];
    memset(dst,0,n*3/4+1);
    for (i=0,j=0;i<n-4;i+=4,j+=3)
    {
        dst[j]=(src[i]<<2) | ((src[i+1]&0x30)>>4);
        dst[j+1]=((src[i+1]&0x0F)<<4) | ((src[i+2]&0x3C)>>2);
        dst[j+2]=((src[i+2]&0x03)<<6) | src[i+3];
    }
    switch (equal_count)//最后四个字符单独处理
    {
        case 0:
            dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4);
            dst[j+1]=((src[n-3]&0x0F)<<4) | ((src[n-2]&0x3C)>>2);
            dst[j+2]=((src[n-2]&0x03)<<6) | src[n-1];
            break;
        case 1:
            dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4);
            dst[j+1]=((src[n-3]&0x0F)<<4) | ((src[n-2]&0x3C)>>2);
            break;
        case 2:
            dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4);
            break;
        default:
            break;
    }
    return dst;
} 

 

二、Quoted-Printable解码

2.1 简介与编解码原理

       Quoted-Printable编码,通常缩写为"Q"编码,其原理是把一个8bit的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3= AC=C4=FA=BA=C3=A3=A1。同Base64一样,Quoted-Printable也是MIME邮件中常用的编码方式之一,一般用在电子邮件的标题。

Quoted-Printable的编码规则

  1. QP编码根据输入字符的数值范围进行编码:输入字符在33-60、62-126范围内的,不需编码直接输出;否则先输出‘=‘,后面跟随两个十六进制数字【0–9 或 A–F (必须使用大写)】表示输入字符的十六进制字节值;
  2. ASCII的水平制表符(tab)与空格符, 十进制为9和32,如果不出现在行尾则可以用其ASCII字符编码直接表示;如果这两个字符出现在行尾,必须QP编码表示为"=09" (tab)或"=20" (space);
  3. 如果数据中包含有意义的行结束标志,必须转换为ASCII回车(CR)换行(LF)序列,既不能用原来的ASCII字符也不能用QP编码的"="转义字符序列。 相反,如果字节值13与10有其它的不是行结束的含义,它们必须QP编码为=0D与=0A;
  4. QP编码后的数据要求每行长度不能超过76个字符。为满足此要求又不改变被编码文本,在QP编码结果的每行末尾加上软换行(soft line break). 即在每行末尾加上一个"=\r\n"序列, 但并不会出现在解码得到的文本中。

Quoted-Printable的解码规则:

  1. 碰到 ‘=‘ 并且后面2个字符均处于0-9 或 A-F 中时,为编码字符序列,否则为未编码字符;【判定是否为编码后字符】
  2. 对于未编码字符,直接输出;
  3. 对于编码字符序列,要将‘=‘后的两个字符转为十六进制数值,然后求出该十六进制数值对应的十进制值,将得到的十进制值转为对应的编码前字节。

本小节部分内容参考

Quote Printable编码原理(Delphi)》http://blog.csdn.net/fengsh998/article/details/8663046

QP(Quote-Printable) 编码http://love-love-l.blog.163.com/blog/static/2107830420113593541737/

2.2 Quoted-Printable解码函数:

/***********************************************************/
/*                       输入参数说明:                      */
/*           pSrc为指向存放输入内容的缓冲区指针                */
/*           pDst为指向存放输出内容的缓冲区指针                */
/*           nSrcLen为输入缓冲区的大小                       */
/*                       输出参数说明:                      */
/*           返回值为输出内容的大小                           */
/***********************************************************/
int Quoted_Decode(char* pszSrc, char* pszDst, int nSrclen)
{
    int i=0;
    int nDstLen;     // 输出的字符计数
    while (i< nSrclen)
    {
        if (strncmp(pszSrc, "=\r\n", 3) == 0)        // 软回车,跳过
        {
            pszSrc += 3;
            i+= 3;
        }
        else
        {
            if (*pszSrc == ‘=‘)        // 编码字节
            {
                sscanf(pszSrc, "=%02X", (unsigned int*)pszDst);
                pszDst++;
                pszSrc += 3;
                i+= 3;
            }
            else        // 非编码字节
            {
                *pszDst++ = *pszSrc++;
                i++;
            } 
            nDstLen++;
        }
    }
    *pszDst = ‘\0‘;         // 输出加个结束符

    return nDstLen;
}

 

三、URL编码解码

3.1 简介与编解码原理

       这种编码方式由于最初用于使URL符合RFC 1738规范【RFC 1738中规定: “只有字母和数字[0-9,a-z,A-Z]、一些特殊符号“$-_.+!*‘(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。”】而被称为URL编码,应该算是HTTP请求报文中最常见的编码了。

URL编码的编码规则:

  1. 当输入字节为字母和数字[0-9,a-z,A-Z]、一些特殊符号“$-_.+!*‘(),”【不包括双引号】、以及某些保留字时,不需编码直接输出;
  2. 否则先输出‘%‘,后面跟随两个十六进制数字【0–9 或 A–F (大小写皆可) 】表示输入字节的十六进制字节值;

URL编码的解码规则:

  1. 碰到 ‘%‘ 并且后面2个字符均处于0-9 或 A-F(大小写皆可)中时,为编码字符序列,否则为未编码字符;【判定是否为编码后字符】
  2. 对于未编码字符,直接输出;
  3. 对于编码字符序列,要将‘%‘后的两个字符转为十六进制数值,然后求出该十六进制数值对应的十进制值,将得到的十进制值转为对应的编码前字节。

3.2 URL解码函数

/***********************************************************/
/*                       输入参数说明:                      */
/*           pSrc为指向存放输入内容的缓冲区指针                */
/*           pDst为指向存放输出内容的缓冲区指针                */
/*           nSrcLen为输入缓冲区的大小                        */
/*                       输出参数说明:                      */
/*           返回值为输出内容的大小                           */
/***********************************************************/
int Url_Decode(const char *pSrc, char *pDst, int nSrcLen)
{
    int nDstLen;    // 输出的字符计数
    int i=0;

    while(i < nSrcLen)
    {
        if(*pSrc == ‘%‘)       // 是编码字节
        {
            sscanf(pSrc, "%%%02x", (unsigned short *)pDst);

            pDst++;
            pSrc += 3;
            i += 3;
        }
        else        // 非编码字节
        {
            *pDst++ = (unsigned char )*pSrc++;
            i++;
        }
        nDstLen++;
    }
    *pDst = ‘\0‘;       // 输出加个结束符

    return nDstLen;
}

 


------本文由CSDN-蚍蜉撼青松【主页:http://blog.csdn.net/howeverpf】原创,转载请注明出处!------

网络协议中常见编码的原理及其解码函数的C++实现,布布扣,bubuko.com

网络协议中常见编码的原理及其解码函数的C++实现

上一篇:Thinking in C++读书笔记---数据抽象


下一篇:C++句柄类