• MFC中一段经典的http协议post图片代码出现中文的错误分析 (转)


    使用VC进行图片上传到服务器,就是构造http的协议包。有段经典的代码,在网上流传并使用。链接如下:
    http://blog.chinaunix.net/u3/94574/showart_1950658.html
    http://read.pudn.com/downloads98/sourcecode/internet/tcp_ip/401601/HotpimUploadDlg.cpp__.htm


    这段代码写的蛮好的,但是当提交的数据中有中文时,便会出现错误。错误的原因主要是这段:
    #ifdef _UNICODE    
          pHTTP->Write(W2A(strPreFileData), strPreFileData.GetLength());   
    #else    
          pHTTP->Write((LPSTR)(LPCSTR)strPreFileData, strPreFileData.GetLength());//写入服务器所需信息    
    #endif    


    W2A宏是获取一个将Wide字符转换为ANSI字符后的一个指针,关于这个宏的意义,可以展开宏进行研究,也可参考这篇文章:
    http://blog.csdn.net/strikebone/archive/2007/11/02/1863523.aspx


    所以如果编译环境为UNICODE,那么W2A就将strPreFileData转换为ANSI字符,并获取这个转换后的指针。
    CString中的GetLength()函数是获取字符串的长度,而不是占用的内存大小,它和sizeof是不一样的。
    比如在ANSI环境下(_MBCS多字节),一个汉字占两个字节。
        CString test(_T("测试test")); 
        int k = test.GetLength();  //结果为8 

        TCHAR  szTest[] = _T("测试test");
        int i = _tcslen(szTest); //结果为8 
        int j = sizeof(szTest); //结果为9,多个结尾符‘\0’。

    那么在UNICODE环境下,一个汉字占两个字节,一个英文也是占两个字节
        CString test(_T("测试test")); 
        int k = test.GetLength();  //结果为6,仅字符占的内存有12个字节。 

        TCHAR  szTest[] = _T("测试test");
        int i = _tcslen(szTest); //结果为6 
        int j = sizeof(szTest); //结果为14,多个结尾符‘\0’也用2个字节。

    可以看到问题了。在UNICODE环境下,即是是结尾符0也是占用两个字节。
    在 不同的环境下,同一个CString.GetLength()返回的值是不一样的。出错的原因要从编码说起。在UNICODE下,任何一个字符都是由2个 字节表示(16位),而ANSI环境下,英文是由1个字节表示,中文则由2个字节表示。所以“测试test”这几个字符,在ANSI环境下占用8个字节的 内存,“测试”占4个,“test”占4个。而在UNICODE环境下,“测试”同样占4个,“test”则要占8个,因为一个英文字符也是占有2个字 节,总共12个字节。所以在UNICODE环境下,GetLength()以2个字节计算一个长度,那么返回6,而ANSI环境下以1个字节计算一个长 度,返回8。


    真正post给网页时,提交的代码却是UTF8的编码,同样服务器返回的也是UTF8的编码(这样说不完全对,服务器接收和返回的编码是可 以指定的,国内很多采用的就是GB2312编码,一般为国际化统一,很多服务器会返回UTF8的网页编码)。UTF8编码又与以上两种编码不一样,英文一 般是一个字节,中文则有可能是两个,有可能是三个字节,它是一种变长的编码。所以如果服务器采用UTF8编码,通过http协议post到网页服务器时, 还要将ANSI或者UNICODE转为UTF8编码,同样接收到数据后,再将UTF8编码转回来。

    这样上面一段发送代码的数据长度计算就有问题。当strPreFileData字符串中有中英文时,strPreFileData.GetLength()获取的长度就不对。

    比 如:假定strPreFileData的数据内容为“测试test”,现在的编程环境是UINCODE下,那么它实际占用内存字节数应该是12个。这样说 也不对,应该说仅这六个字占用的内存为12个字节,CString还要用额外的12个字节存储引用次数、串长度和实际分配的内存长度,总的CString 现在占用的内存应该是24个字节。那么当把strPreFileData转为ANSI码时,占用8个字节内存。发送数据时,应该发送这8个字节的内容才对,而GetLength()返回的长度是6,结果只发送了6个字节的内容,还有2个字节没有发送,这样就产生问题了。

    下面这篇文章我写了一个post的类,包含编码转换。有兴趣可以看一下,也可以直接使用,用参数控制编码传送。

     /Html/diannaojishu/2009-10/4905858580.html

    现在来实例测试一下采用原始方法,即使用W2A宏产生的数据问题。

    在_MBCS环境下,提交数据“测试test”,抓包如下:

    ============================================

    POST /posttest.asp HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    User-Agent: PostSend
    Host: www.lilu.name
    Content-Length: 8
    Cache-Control: no-cache
    Cookie: lstat_bc_1053476=6357572592769926607; lstat_bc_1242668=840816305580142630; rtime=33; ltime=1256698458890; cnzz_eid=18096627-1249021004-http://www1.js.vnet.cn/push/0908/kxp/index.asp; cnzz_a481312=2; vw481312=:41895096:83815771:; sin481312=none; ASPSESSIONIDCQCCDDBA=MIHLNIMDAIBHMFGOBNLMJJML

    测试test

    =============================================

    在_UNICODE环境下,提交数据“测试test”,抓包如下:

    =============================================

    POST /posttest.asp HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    User-Agent: PostSend
    Host: www.lilu.name
    Content-Length: 6
    Cache-Control: no-cache
    Cookie: lstat_bc_1053476=6357572592769926607; lstat_bc_1242668=840816305580142630; rtime=33; ltime=1256707419968; cnzz_eid=18096627-1249021004-http://www1.js.vnet.cn/push/0908/kxp/index.asp; cnzz_a481312=3; vw481312=:41895096:83815771:16206317:; sin481312=

    测试te

    =================================================

    可以看到,在UNICODE环境下,就出现了丢包问题。_MBCS环境下post的包有8个字节,“测试test”这几个字都提交上去了,而在 UNICODE环境下,post的包只有6个字节,丢失了最后的“st”两个字节。这就是由于使用GetLength()引起的问题。

    所以在使用此宏是要注意字节的实际长度!那段上传代码如果全部是英文,就不会出现丢包,如果文件名中包含中文,或者数据中包含中文,就会出现丢包。

  • 相关阅读:
    HDU 5791 Two (DP)
    POJ 1088 滑雪 (DPor记忆化搜索)
    LightOJ 1011
    POJ 1787 Charlie's Change (多重背包 带结果组成)
    HDU 5550 Game Rooms (ccpc2015 K)(dp)
    HDU 5542 The Battle of Chibi (ccpc 南阳 C)(DP 树状数组 离散化)
    HDU 5543 Pick The Sticks (01背包)
    HDU 5546 Ancient Go (ccpc2015南阳G)
    NB-IoT的DRX、eDRX、PSM三个模式 (转载,描述的简单易懂)
    MQTT 嵌入式端通讯协议解析(转)
  • 原文地址:https://www.cnblogs.com/yaoliang11/p/1860307.html
Copyright © 2020-2023  润新知