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();            
    }

        【参考资料】:

  • 相关阅读:
    leetcode 279. Perfect Squares
    leetcode 546. Remove Boxes
    leetcode 312. Burst Balloons
    leetcode 160. Intersection of Two Linked Lists
    leetcode 55. Jump Game
    剑指offer 滑动窗口的最大值
    剑指offer 剪绳子
    剑指offer 字符流中第一个不重复的字符
    leetcode 673. Number of Longest Increasing Subsequence
    leetcode 75. Sort Colors (荷兰三色旗问题)
  • 原文地址:https://www.cnblogs.com/hoodlum1980/p/2521500.html
Copyright © 2011-2022 走看看