zoukankan      html  css  js  c++  java
  • C/C++版实现(Base64, UrlEncode等)

        【原创性声明】:本文无实质性创新性内容,性质属于技术总结,内容是基于已有知识或定义的代码实现。文中的代码是我根据其他代码或者网络上的资料,写出的自己的版本。因为网络上的代码 C/C++ 版本的较少,或者本身不一定是最合适最容易使用的。所以我写的代码基本是以 C 语言和 C 字符串处理为主的,因此它也可以直接在 C++ 的项目中使用。

        (1)Base64 编码。Base64 编码是把每 3 个字节转换成 4 个ascii 字符(根据字符表映射)。把文本编码后,对人来说难以直接阅读。结尾不足时可能有一到两个 "=" 字符的补齐。

    code_Base64
    //以下是 Base64.h 的内容:
    
    size_t Base64_Decode(char *pDest, const char *pSrc, size_t srclen);
    size_t Base64_Encode(char *pDest, const char *pSrc, size_t srclen);
    
    //以下是 Base64.cpp 的内容:
    
    BYTE Decode_GetByte(char c);
    char Encode_GetChar(BYTE num);
    
    //===================================
    //    Base64 解码
    //===================================
    BYTE Decode_GetByte(char c)
    {
        if(c == '+')
            return 62;
        else if(c == '/')
            return 63;
        else if(c <= '9')
            return (BYTE)(c - '0' + 52);
        else if(c == '=')
            return 64;
        else if(c <= 'Z')
            return (BYTE)(c - 'A');
        else if(c <= 'z')
            return (BYTE)(c - 'a' + 26);
        return 64;
    }
    
    //解码
    size_t Base64_Decode(char *pDest, const char *pSrc, size_t srclen)
    {
        BYTE input[4];
        size_t i, index = 0;
        for(i = 0; i < srclen; i += 4)
        {
            //byte[0]
            input[0] = Decode_GetByte(pSrc[i]);
            input[1] = Decode_GetByte(pSrc[i + 1]);
            pDest[index++] = (input[0] << 2) + (input[1] >> 4);
            
            //byte[1]
            if(pSrc[i + 2] != '=')
            {
                input[2] = Decode_GetByte(pSrc[i + 2]);
                pDest[index++] = ((input[1] & 0x0f) << 4) + (input[2] >> 2);
            }
    
            //byte[2]
            if(pSrc[i + 3] != '=')
            {
                input[3] = Decode_GetByte(pSrc[i + 3]);
                pDest[index++] = ((input[2] & 0x03) << 6) + (input[3]);
            }            
        }
    
        //null-terminator
        pDest[index] = 0;
        return index;
    }
    
    //===================================
    //    Base64 编码
    //===================================
    char Encode_GetChar(BYTE num)
    {
        return 
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789"
            "+/="[num];
    }
    
    //编码
    size_t Base64_Encode(char *pDest, const char *pSrc, size_t srclen)
    {
        BYTE input[3], output[4];
        size_t i, index_src = 0, index_dest = 0;
        for(i = 0; i < srclen; i += 3)
        {
            //char [0]
            input[0] = pSrc[index_src++];
            output[0] = (BYTE)(input[0] >> 2);
            pDest[index_dest++] = Encode_GetChar(output[0]);
    
            //char [1]
            if(index_src < srclen)
            {
                input[1] = pSrc[index_src++];
                output[1] = (BYTE)(((input[0] & 0x03) << 4) + (input[1] >> 4));
                pDest[index_dest++] = Encode_GetChar(output[1]);
            }
            else
            {
                output[1] = (BYTE)((input[0] & 0x03) << 4);
                pDest[index_dest++] = Encode_GetChar(output[1]);
                pDest[index_dest++] = '=';
                pDest[index_dest++] = '=';
                break;
            }
            
            //char [2]
            if(index_src < srclen)
            {
                input[2] = pSrc[index_src++];
                output[2] = (BYTE)(((input[1] & 0x0f) << 2) + (input[2] >> 6));
                pDest[index_dest++] = Encode_GetChar(output[2]);
            }
            else
            {
                output[2] = (BYTE)((input[1] & 0x0f) << 2);
                pDest[index_dest++] = Encode_GetChar(output[2]);
                pDest[index_dest++] = '=';
                break;
            }
    
            //char [3]
            output[3] = (BYTE)(input[2] & 0x3f);
            pDest[index_dest++] = Encode_GetChar(output[3]);
        }
        //null-terminator
        pDest[index_dest] = 0;
        return index_dest;
    }

        (2)UrlEncode (百分号编码)。方法是把输入的字符串先用 UTF-8 编码,然后把基本字符以外的字节用百分号加16进制的形式编码。UrlEncode 的最后一个参数含义是,编码结果中的16进制字符是否采用大写字母表示。

    code_UrlEncode
    BOOL UrlEncode(const char* szSrc, char* pBuf, int cbBufLen, BOOL bUpperCase);
    BOOL UrlDecode(const char* szSrc, char* pBuf, int cbBufLen);
    
    //百分号编码
    //http://zh.wikipedia.org/zh-cn/%E7%99%BE%E5%88%86%E5%8F%B7%E7%BC%96%E7%A0%81
    
    BOOL UrlEncode(const char* szSrc, char* pBuf, int cbBufLen, BOOL bUpperCase)
    {
        if(szSrc == NULL || pBuf == NULL || cbBufLen <= 0)
            return FALSE;
    
        size_t len_ascii = strlen(szSrc);
        if(len_ascii == 0)
        {
            pBuf[0] = 0;
            return TRUE;
        }
        
        //先转换到UTF-8
        char baseChar = bUpperCase ? 'A' : 'a';
        int cchWideChar = MultiByteToWideChar(CP_ACP, 0, szSrc, len_ascii, NULL, 0);
        LPWSTR pUnicode = (LPWSTR)malloc((cchWideChar + 1) * sizeof(WCHAR));
        if(pUnicode == NULL)
            return FALSE;
        MultiByteToWideChar(CP_ACP, 0, szSrc, len_ascii, pUnicode, cchWideChar + 1);
    
        int cbUTF8 = WideCharToMultiByte(CP_UTF8, 0, pUnicode, cchWideChar, NULL, 0, NULL, NULL);
        LPSTR pUTF8 = (LPSTR)malloc((cbUTF8 + 1) * sizeof(CHAR));
        if(pUTF8 == NULL)
        {
            free(pUnicode);
            return FALSE;
        }
        WideCharToMultiByte(CP_UTF8, 0, pUnicode, cchWideChar, pUTF8, cbUTF8 + 1, NULL, NULL);
        pUTF8[cbUTF8] = '\0';
    
        unsigned char c;
        int cbDest = 0; //累加
        unsigned char *pSrc = (unsigned char*)pUTF8;
        unsigned char *pDest = (unsigned char*)pBuf;
        while(*pSrc && cbDest < cbBufLen - 1)
        {
            c = *pSrc;
            if(isalpha(c) || isdigit(c) || c == '-' || c == '.' || c == '~')
            {
                *pDest = c;
                ++pDest;
                ++cbDest;
            }
            else if(c == ' ')
            {
                *pDest = '+';
                ++pDest;
                ++cbDest;
            }
            else
            {
                //检查缓冲区大小是否够用?
                if(cbDest + 3 > cbBufLen - 1)
                    break;
                pDest[0] = '%';
                pDest[1] = (c >= 0xA0) ? ((c >> 4) - 10 + baseChar) : ((c >> 4) + '0');
                pDest[2] = ((c & 0xF) >= 0xA)? ((c & 0xF) - 10 + baseChar) : ((c & 0xF) + '0');
                pDest += 3;
                cbDest += 3;
            }
            ++pSrc;
        }
        //null-terminator
        *pDest = '\0';
        free(pUnicode);
        free(pUTF8);
        return TRUE;
    }
    
    //解码后是utf-8编码
    BOOL UrlDecode(const char* szSrc, char* pBuf, int cbBufLen)
    {
        if(szSrc == NULL || pBuf == NULL || cbBufLen <= 0)
            return FALSE;
    
        size_t len_ascii = strlen(szSrc);
        if(len_ascii == 0)
        {
            pBuf[0] = 0;
            return TRUE;
        }
        
        char *pUTF8 = (char*)malloc(len_ascii + 1);
        if(pUTF8 == NULL)
            return FALSE;
    
        int cbDest = 0; //累加
        unsigned char *pSrc = (unsigned char*)szSrc;
        unsigned char *pDest = (unsigned char*)pUTF8;
        while(*pSrc)
        {
            if(*pSrc == '%')
            {
                *pDest = 0;
                //高位
                if(pSrc[1] >= 'A' && pSrc[1] <= 'F')
                    *pDest += (pSrc[1] - 'A' + 10) * 0x10;
                else if(pSrc[1] >= 'a' && pSrc[1] <= 'f')
                    *pDest += (pSrc[1] - 'a' + 10) * 0x10;
                else
                    *pDest += (pSrc[1] - '0') * 0x10;
    
                //低位
                if(pSrc[2] >= 'A' && pSrc[2] <= 'F')
                    *pDest += (pSrc[2] - 'A' + 10);
                else if(pSrc[2] >= 'a' && pSrc[2] <= 'f')
                    *pDest += (pSrc[2] - 'a' + 10);
                else
                    *pDest += (pSrc[2] - '0');
    
                pSrc += 3;
            }
            else if(*pSrc == '+')
            {
                *pDest = ' ';
                ++pSrc;
            }
            else
            {
                *pDest = *pSrc;
                ++pSrc;
            }
            ++pDest;
            ++cbDest;
        }
        //null-terminator
        *pDest = '\0';
        ++cbDest;
    
        int cchWideChar = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pUTF8, cbDest, NULL, 0);
        LPWSTR pUnicode = (LPWSTR)malloc(cchWideChar * sizeof(WCHAR));
        if(pUnicode == NULL)
        {
            free(pUTF8);
            return FALSE;
        }
        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pUTF8, cbDest, pUnicode, cchWideChar);
        WideCharToMultiByte(CP_ACP, 0, pUnicode, cchWideChar, pBuf, cbBufLen, NULL, NULL);
        free(pUTF8);
        free(pUnicode);
        return TRUE;
    }

        (3)获取本机 IP 地址和网卡物理地址。可能有多个网络适配器,但这里只是给出第一个适配器的结果。根据网络资料,获取网卡物理地址有多种方法。已知 IP 地址获取其物理地址可以使用 SendARP 方法。但对于本机来说,可以直接用 GetAdaptersAddresses 更适合,其返回结果为链表(list)形式。这里对 IP 默认为 IPv4(4个字节),网卡物理地址通常为 6 个字节。

    code_GetMacAddress
    #include <Winsock2.h>
    #include <IPHlpApi.h> //for GetAdaptersAddresses
    
    //获取本机的IP地址
    BOOL GetIPAddress(unsigned char* buffer)
    {
        int bRet;
        char hostname[256];
        WSADATA wsadata; 
        struct hostent *pHost;
        struct in_addr *pAddr;
        WORD dwVersionRequested = MAKEWORD( 2, 2 );
        
        bRet= WSAStartup(dwVersionRequested, &wsadata);
        bRet= gethostname(hostname, sizeof(hostname));
        pHost = gethostbyname(hostname);
    
        if(pHost != NULL && pHost->h_addr_list[0] != NULL) 
        {
            pAddr = (struct in_addr*)(pHost->h_addr_list[0]);
            buffer[0] = pAddr->s_net;
            buffer[1] = pAddr->s_host;
            buffer[2] = pAddr->s_lh;
            buffer[3] = pAddr->s_impno;
            bRet = TRUE;
        }
        else
        {
            //取不到IP地址,则赋值默认值 255.255.255.255
            memset(buffer, 0xff, 4);
            bRet = FALSE;
        }
        WSACleanup();
        return bRet;
    }
    
    //参数pMacAddr应该是8个字节的数组
    BOOL GetMacAddress(unsigned char* pMacAddr)
    {
        DWORD nRet;
        //只查询物理地址
        DWORD nFlags = GAA_FLAG_SKIP_UNICAST 
            | GAA_FLAG_SKIP_ANYCAST
            | GAA_FLAG_SKIP_FRIENDLY_NAME
            | GAA_FLAG_SKIP_MULTICAST
            | GAA_FLAG_SKIP_DNS_SERVER;
    
        ULONG bufLen = 1024;
        PIP_ADAPTER_ADDRESSES pAdapterAddr = (PIP_ADAPTER_ADDRESSES)malloc(bufLen);
        if(pAdapterAddr == NULL)
            return FALSE;
    
        //AF_INET: return only IPv4 addresses.
        nRet = GetAdaptersAddresses(AF_INET, nFlags,  NULL, pAdapterAddr, &bufLen);
        if(nRet == ERROR_BUFFER_OVERFLOW)
        {
            pAdapterAddr = (PIP_ADAPTER_ADDRESSES)realloc(pAdapterAddr, bufLen);
            if(pAdapterAddr == NULL)
                return FALSE;
    
            nRet = GetAdaptersAddresses(AF_INET, nFlags,  NULL, pAdapterAddr, &bufLen);
        }
    
        if(nRet == ERROR_SUCCESS)
        {
            memcpy(pMacAddr, &pAdapterAddr->PhysicalAddress, pAdapterAddr->PhysicalAddressLength);
            free(pAdapterAddr);
            return TRUE;
        }
        else
        {
            //ff-ff-ff-ff-ff-ff: 表示获取失败(未知)
            memset(pMacAddr, 0xff, 6);
            free(pAdapterAddr);
            return FALSE;
        }
    }

        (4)使用 SMTP 发送邮件。本文是参考看雪论坛上某文章中的代码。如果发送中文,应该在传输时,指定文本使用的编码,以防止接收端解释成乱码。以下代码引用了(1)中的 Base64 编码。

    code_SendMail
    #include <Winsock2.h>
    
    typedef struct _SMTPINFO
    {
        char Server[32];
        int Port;
        char UserName[16];
        char Password[16];
        char From[32];
        char To[32];
        char Subject[32];
        char Msg[256];
    } SMTPINFO, *LPSMTPINFO;
    
    
    BOOL Talk(SOCKET sockid, const char *szOkCode, char *pSend);
    BOOL SendMail(const LPSMTPINFO pInfo);
    
    //szOkCode: 是前一条命令成功时,预期的服务器的返回码
    BOOL Talk(SOCKET sockid, const char *szOkCode, char *pSend)
    {
        const int buflen = 256;
        char buf[buflen];
    
        //接收返回信息
        if (recv(sockid, buf, buflen, 0) == SOCKET_ERROR)
            return FALSE;
    
        if (strncmp(buf, szOkCode, strlen(szOkCode)) != 0)
            return FALSE;
    
        //发送命令
        if (lstrlen(pSend) > 0)
        {
            WSABUF DataBuf;
            DataBuf.len = lstrlen(pSend);
            DataBuf.buf = pSend;
    
            DWORD dwS;
            if(WSASend(sockid, &DataBuf, 1, &dwS, 0, 0, 0))
                return FALSE;
        }
        return TRUE;
    }
    
    BOOL SendMail(const LPSMTPINFO pInfo)
    {
        char buf[1024];
        //准备网络连接
        WSADATA wsadata;
    
        if(WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
            return FALSE;
    
        //创建套接字
        SOCKET sockid;
        if ((sockid = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
            WSACleanup();
            return FALSE;
        }
    
        //得到smtp服务器ip
        struct hostent *phostent = gethostbyname(pInfo->Server);
        struct sockaddr_in addr;
    
        CopyMemory(&addr.sin_addr.S_un.S_addr,
            phostent->h_addr_list[0],
            sizeof(addr.sin_addr.S_un.S_addr));
    
        addr.sin_family = AF_INET;
        addr.sin_port = htons(pInfo->Port);
        ZeroMemory(&addr.sin_zero, 8);
    
        //连接服务器
        if(connect(sockid, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
            goto STOP;
    
        //EHLO 是对 HELO 的扩充命令。
        strcpy(buf, "EHLO hoodlum1980\r\n");
        if(!Talk(sockid, "220", buf))
            goto STOP;
    
        strcpy(buf, "AUTH LOGIN\r\n");
        if(!Talk(sockid, "250", buf))
            goto STOP;
    
        size_t userlen = lstrlen(pInfo->UserName);
        size_t passlen = lstrlen(pInfo->Password);
        Base64_Encode(buf, pInfo->UserName, userlen);
        strcat(buf, "\r\n");
        if(!Talk(sockid, "334", buf))
            goto STOP;
    
        Base64_Encode(buf, pInfo->Password, passlen);
        strcat(buf, "\r\n");
        if (!Talk(sockid, "334", buf))
            goto STOP;
    
        //ZeroMemory(buf, buflen);
        wsprintf(buf, "MAIL FROM:<%s>\r\n", pInfo->From);
        if(!Talk(sockid, "235", buf))
            goto STOP;
    
        wsprintf(buf, "RCPT TO:<%s>\r\n", pInfo->To);
        if(!Talk(sockid, "250", buf))
            goto STOP;
    
        strcpy(buf, "DATA\r\n");
        if (!Talk(sockid, "250", buf))
            goto STOP;
    
        char szContent[512];
        Base64_Encode(szContent, pInfo->Msg, strlen(pInfo->Msg));
        wsprintf(buf, 
            "MIME-Version:1.0\r\nContent-Type:text/plain; charset=gb2312\r\n"
            "Content-Transfer-Encoding:base64\r\nContent-Language:zh-cn\r\n"
            "TO: %s\r\nFROM: %s\r\nSUBJECT: %s\r\n\r\n%s\r\n.",
            pInfo->To, pInfo->From, pInfo->Subject,    szContent);
    
    
        if(Talk(sockid, "354", buf))
            goto STOP;
    
        strcpy(buf, "QUIT\r\n");
        if(!Talk(sockid, "250", buf))
            goto STOP;
    
        strcpy(buf, "");
        if(!Talk(sockid, "221", buf))
            goto STOP;
        else
        {
            closesocket(sockid);
            WSACleanup();
            return TRUE;
        }
    
    STOP:
        closesocket(sockid);
        WSACleanup();
        return FALSE;
    }
    
    // 使用范例: 
    SMTPINFO info;
    strcpy(info.Server,"smtp.***.com");
    info.Port = 25;
    strcpy(info.UserName,"XXXXX@***.com");
    strcpy(info.Password,"*****");
    strcpy(info.From, "XXXXX@***.com");
    strcpy(info.To, "YYYYY@YYY.com");
    strcpy(info.Subject, "邮件标题");
    strcpy(info.Msg, "邮件内容。");
    SendMail(&info);

        (5)以 Post 形式发送 HTTP 请求。如果使用 C# 则可以使用 HttpRequest。由于是测试版本,以下代码在读取服务器响应时假设服务器的响应是短文本,因此没有判断服务器的响应是否已全部读取,并不是非常完善。在实际应用中应完善后使用。

    code_HttpSendRequest
    #include <Wininet.h>
    BOOL SendHttpPost()
    {
        HINTERNET hInternet = InternetOpen(_T("HttpPostTest"),
            INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    
        if(hInternet == NULL)
            return FALSE;
    
        HINTERNET hConnect = InternetConnect(hInternet,
            _T("www.xxx.com"),
            INTERNET_DEFAULT_HTTP_PORT,
            NULL, //user
            NULL, //password
            INTERNET_SERVICE_HTTP, 0, 0);
    
        if(hConnect == NULL)
        {
            InternetCloseHandle(hInternet);
            return FALSE;
        }
    
        LPCTSTR szTypes[] = { _T("*/*"), NULL };
        TCHAR szData[1024], szResponse[1024];
        TCHAR szContent[1024], szTemp[128];
    
        //全英文的ascii字符集是UTF-8的子集,所以下面的字符串也可以看着是 UTF-8 编码的。
        _tcscpy(szContent, _T("user=xxx&password=xxx"));
    
        DWORD dwBytesAvailable = 0, dwBytesRead = 0; 
    
        HINTERNET hRequest = HttpOpenRequest(hConnect,
            _T("POST"),
            _T("/xxx/xxx.aspx"),
            NULL, //version: "HTTP/1.1"
            _T("http://www.xxx.com/xxx/xxx.aspx"), //referer
            szTypes, //types
            INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_RELOAD, 0);
    
        if(hRequest == NULL)
        {
            InternetCloseHandle(hConnect);
            InternetCloseHandle(hInternet);
            return FALSE;
        }
    
        BOOL bSuccess;
        
        TCHAR headers[512];
        _stprintf(headers, _T("Content-Length: %ld\r\n"), _tcslen(szContent));
        _tcscat(headers, 
            _T("Content-Type: application/x-www-form-urlencoded\r\n")
            _T("xxx: yyy\r\n")
            );
    
        bSuccess = HttpSendRequest(hRequest,
            headers, //additional header
            _tcslen(headers), //headersLength
            szContent,
            _tcslen(szContent));
    
        TCHAR szBuffer[1024];
        DWORD bufLen = sizeof(szBuffer);
        DWORD dwIndex = 0;
        if(bSuccess)
        {
            //查询服务器返回的headers信息,内含传输内容使用的编码。
            HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, 
                szBuffer,
                &bufLen, &dwIndex);
    
            InternetQueryDataAvailable(hRequest, &dwBytesAvailable, 0, 0);
            bSuccess = InternetReadFile(hRequest, szResponse, sizeof(szResponse), &dwBytesRead);
    
            int count = MultiByteToWideChar(CP_UTF8, 0, szResponse, dwBytesRead, szDataUnicode, sizeof(szDataUnicode)/sizeof(szDataUnicode[0]));
            count = WideCharToMultiByte(CP_ACP, 0, szDataUnicode, count, szData, sizeof(szData)/sizeof(szData[0]), NULL, NULL);
            szData[count] = '\0';
        }
    
        InternetCloseHandle(hRequest);
        InternetCloseHandle(hConnect);
        InternetCloseHandle(hInternet);
        return bSuccess;
    }

        

        以下是 java 版本的类似代码:

    code_SendRequest_java
    public String SendRequest() 
    {
        //发送post请求获取签名
        String postData = "user=%s&password=%s";
        byte[] postBytes = postData.getBytes();
        URL url = new URL("http://www.xxx.com/xxx/xxx.aspx");
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Length", String.valueOf(postBytes.length));
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        connection.setRequestProperty("xxx", "yyy");
        connection.setUseCaches(false);
        connection.connect();
        
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.write(postBytes);
        out.flush();
        out.close(); 
        
        StringBuffer buf = new StringBuffer(960);
        int responseCode = connection.getResponseCode();
        if(responseCode == 200)
        {
            //获取服务器返回的数据
            InputStream stream = connection.getInputStream();
            BufferedReader bufReader = new BufferedReader(new InputStreamReader(stream));
            String line = "";
            while((line = bufReader.readLine()) != null)
                buf.append(line);
        }
        return buf.toString();            
    }

        【参考资料】:

  • 相关阅读:
    [转贴]中国铁塔发布2020年中期财报:营收、利润双增,高效支撑5G规模建设
    struts2总结二:第一个简单的struts2程序
    struts2总结一:MVC设计模式
    解决eclipse报PermGen space内存溢出异常的问题
    java中new关键字和newInstance()方法有什么区别?
    java反射机制
    在easyui中如何修改combobox的下拉框的高度为自适应高度
    百度UEditor从word复制粘贴公式
    百度富文本编辑器从word复制粘贴图片
    HTML编辑器从word复制粘贴图片
  • 原文地址:https://www.cnblogs.com/hoodlum1980/p/2521500.html
Copyright © 2011-2022 走看看