zoukankan      html  css  js  c++  java
  • 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()引起的问题。

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

  • 相关阅读:
    十万个为什么 —— 理化篇
    地行、地貌
    工业镜头基础知识整理
    parfor —— matlab 下的并行循环
    浅谈设计模式在GIS中的应用
    android ellipsize 属性详解
    Servlet 学习总结-1
    搜索引擎有用的外链建设的方式
    Servlet 学习总结-2
    如何用批处理命令批量配对重命名
  • 原文地址:https://www.cnblogs.com/yaoliang11/p/1860307.html
Copyright © 2011-2022 走看看