找回密码
 加入
搜索
查看: 17968|回复: 15

[系统综合] HttpSendRequestEx 中如何创建及填充 INTERNET_BUFFERS 结构

 火.. [复制链接]
发表于 2011-3-11 22:28:44 | 显示全部楼层 |阅读模式
本帖最后由 republican 于 2011-3-12 08:43 编辑

我的目的是使用WinInet分步POST数据。

对于WinInet来说,其Http传输结构如下:

其中,HttpSendRequest就已经包含了发送及接受的过程,不能再在之后调用InternetWriteFile继续发送数据。

如果想分步向服务器传输请求,只能使用HttpSendRequestEx发送数据。但其中有个INTERNET_BUFFERS结构,我不是很明白,所以在此发帖寻求帮助,谢谢。

假设我要提交的Header为"xxxx",内容为"xxx"(代码中有),那么下述代码应该如何修改?附带一篇讲述WinInet传输过程的文章

(数据没有正确填充,或是结构有问题。)
(去除71行,将发现BufferLength为0,Header没有填充)
#include <WinINet.au3>
#include <array.au3>

;~ typedef struct _INTERNET_BUFFERS {
;~   DWORD             dwStructSize;
;~   _INTERNET_BUFFERS *Next;
;~   LPCTSTR           lpcszHeader;
;~   DWORD             dwHeadersLength;
;~   DWORD             dwHeadersTotal;
;~   LPVOID            lpvBuffer;
;~   DWORD             dwBufferLength;
;~   DWORD             dwBufferTotal;
;~   DWORD             dwOffsetLow;
;~   DWORD             dwOffsetHigh;
;~ } INTERNET_BUFFERS, * LPINTERNET_BUFFERS;


;~ dwStructSize
;~ Size of the structure, in bytes. 

;~ Next
;~ Pointer to the next INTERNET_BUFFERS structure. 

;~ lpcszHeader
;~ Pointer to a string value that contains the headers. This member can be NULL. 

;~ dwHeadersLength
;~ Size of the headers, in TCHARs, if lpcszHeader is not NULL. 

;~ dwHeadersTotal
;~ Size of the headers, if there is not enough memory in the buffer. 

;~ lpvBuffer
;~ Pointer to the data buffer. 

;~ dwBufferLength
;~ Size of the buffer, in TCHARs, if lpvBuffer is not NULL. 

;~ dwBufferTotal
;~ Total size of the resource, in bytes. 

;~ dwOffsetLow
;~ Reserved; do not use. 

;~ dwOffsetHigh
;~ Reserved; do not use.

;~         Global Const $tagINTERNET_BUFFERS =         " dword StructSize;" & _
;~                                                                                         " ptr Next;" & _
;~                                                                                         " ptr Header;" & _
;~                                                                                         " dword HeadersLength;" & _
;~                                                                                         " dword HeadersTotal;" & _
;~                                                                                         " ptr Buffer;" & _
;~                                                                                         " dword BufferLength;" & _
;~                                                                                         " dword BufferTotal;" & _
;~                                                                                         " dword Offset[2]"


; ====请先填写账号密码
$Name = ""
$PassWord = ""

$Myopen = _WinINet_InternetOpen()
$hConnect = _WinINet_InternetConnect($Myopen,$INTERNET_SERVICE_HTTP,'www.autoitx.com')

$hRequest = _WinINet_HttpOpenRequest($hConnect,'POST',"/logging.php?action=login&loginsubmit=yes&floatlogin=yes&inajax=1",$INTERNET_FLAG_NO_CACHE_WRITE)

$Context = "formhash=17b2166a&referer=http%3A%2F%2Fwww.autoitx.com%2F&loginfield=username&username="&$Name&"&password="&$PassWord&"&&questionid=0&answer="
$Header = "Content-Type: application/x-www-form-urlencoded" & @CRLF & 'Content-Length: '& BinaryLen(StringToBinary($Context))

_WinINet_HttpAddRequestHeaders($hRequest, $Header , $HTTP_ADDREQ_FLAG_ADD + $HTTP_ADDREQ_FLAG_REPLACE)                ;我想要的是将本行去除,并去除上行的'Content-Length',全部交由下面的DllStruct处理
; -------- 本过程数据填充不正确
        Local $tINTERNET_BUFFERS[2] = [DllStructCreate($tagINTERNET_BUFFERS),                DllStructCreate('Char HeaderChar['&StringLen($Header) +1 &'];BYTE DataBuffer['&BinaryLen($Context)&']')                ]

        DllStructSetData($tINTERNET_BUFFERS[0], "Header", DllStructGetPtr($tINTERNET_BUFFERS[1], 'HeaderChar'))
        DllStructSetData($tINTERNET_BUFFERS[0], "Buffer", DllStructGetPtr($tINTERNET_BUFFERS[1], 'DataBuffer'))
        DllStructSetData($tINTERNET_BUFFERS[0], "HeadersLength", StringLen($Header))
        DllStructSetData($tINTERNET_BUFFERS[0], "BufferLength", BinaryLen($Context))
         
        DllStructSetData($tINTERNET_BUFFERS[1], "HeaderChar", $Header)
        DllStructSetData($tINTERNET_BUFFERS[1], "DataBuffer", $Context)

        DllStructSetData($tINTERNET_BUFFERS[0], "StructSize", DllStructGetSize($tINTERNET_BUFFERS[0]))
; ---End Of StructCreate
_WinINet_HttpSendRequestEx($hRequest, $tINTERNET_BUFFERS[0])
ConsoleWrite(_WinAPI_GetLastError()& '----' )

_WinINet_InternetWriteFile($hRequest, StringToBinary($Context))
ConsoleWrite(_WinAPI_GetLastError()& '----' )
_WinINet_HttpEndRequest($hRequest)
ConsoleWrite(_WinAPI_GetLastError()& '----' )

$rContext = _WinINet_HttpQueryInfo($hRequest,$HTTP_QUERY_RAW_HEADERS_CRLF)
MsgBox(64,"Set-Cookies",_WinInet_DecodeHeader($rContext))

$Test = _WinINet_InternetReadFile($hRequest,1024 * 64)
MsgBox(0,"网页内容:",BinaryToString($Test))
_WinINet_InternetCloseHandle($hRequest)

; ===下次执行前,请最好在IE中注销登录
_WinINet_InternetCloseHandle($hConnect)
_WinINet_InternetCloseHandle($Myopen)


Func _WinInet_DecodeHeader($HeaderArray)
        Local $t,$UTF_16_Text
        $t = DllStructCreate('BYTE TEST['&$HeaderArray[1]&']',DllStructGetPtr($HeaderArray[0]))
        $UTF_16_Text = DllStructGetData($t,1)
        Return BinaryToString($UTF_16_Text,2)
EndFunc

Func _WinAPI_GetLastError($curErr=@error, $curExt=@extended)
        Local $aResult = DllCall("kernel32.dll", "dword", "GetLastError")
        Return SetError($curErr, $curExt, $aResult[0])
EndFunc   ;==>_WinAPI_GetLastError

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
发表于 2011-3-11 22:36:47 | 显示全部楼层
dword StructSize; ptr Next; ptr Header; dword HeadersLength; dword HeadersTotal; ptr Buffer; dword BufferLength; dword BufferTotal; dword Offset[2]
这样是不是容易理解些?
 楼主| 发表于 2011-3-11 22:46:37 | 显示全部楼层
回复 2# ceoguang

其实问题是我对这个不是很了解,哪些该填充哪些数据不是很明白。

再说,INTERNET_BUFFERS 有一个 Buffer 结构的指针,此时不是可以直接填充数据么,那么_WinINet_InternetWriteFile又要写什么数据?
发表于 2011-3-11 23:08:42 | 显示全部楼层
我是这样理解的
dwStructSize
下一个结构的大小
Next
下一个结构的指针地址
lpcszHeader
http头(指针)
dwHeadersLength
http头的大小
Buffer
缓冲区指针
BufferLength
缓冲区大小
dwBufferTotal
要发送的总数据大小
后面两个参数为保留值
 楼主| 发表于 2011-3-11 23:19:25 | 显示全部楼层
回复 4# ceoguang
嗯,官方是这么解释的。

1. 首先有个问题,Headerlength 与 HeaderTotal 的区别。
2. header 与 buffer 的数据填充是否像我代码理解的那样操作。
3. structSize 不清楚要怎么计算。
发表于 2011-3-11 23:33:47 | 显示全部楼层
1 哪里来的HeaderTotal?
2 你代码呢?
3 DllStructGetSize
另外,你更新的1#的后一个代码想要怎样修改?
 楼主| 发表于 2011-3-12 08:37:08 | 显示全部楼层
本帖最后由 republican 于 2011-3-12 08:47 编辑

回复 6# ceoguang

现在更新了一楼的代码, 希望你能重新看看,谢谢。
发表于 2011-3-12 15:11:27 | 显示全部楼层
本帖最后由 ceoguang 于 2011-3-12 15:12 编辑
回复  ceoguang

现在更新了一楼的代码, 希望你能重新看看,谢谢。
republican 发表于 2011-3-12 08:37

很抱歉没细看
另外我在4#的说法有很多是错误的
重新理解一次
typedef struct _INTERNET_BUFFERS {
  DWORD             dwStructSize;INTERNET_BUFFERS结构大小(DllStructCreate($tagINTERNET_BUFFERS)的结构大小)
  _INTERNET_BUFFERS *Next;//下一个缓冲区的地址
  LPCTSTR           lpcszHeader;//http头(可以为空)
  DWORD             dwHeadersLength;//http头的大小
  DWORD             dwHeadersTotal;//同上,又或者理解为Header缓冲区大小不够用的时候会在这里取
  LPVOID            lpvBuffer;//要传输的数据?(那InternetWriteFile用来做什么?)
  DWORD             dwBufferLength;//解释同http头
  DWORD             dwBufferTotal;//解释同http头
  DWORD             dwOffsetLow;//保留,不设置
  DWORD             dwOffsetHigh;//保留,不设置
} INTERNET_BUFFERS, * LPINTERNET_BUFFERS;
搜索了N多资料,Header都是可以在INTERNET_BUFFERS中填充的,但au3似乎只能通过HttpAddRequestHeaders进行设置,如果强行设置的话,服务器可能会返回http400错误.
试过N种填充方式,HttpSendRequestEx只有使用不填充的INTERNET_BUFFERS效果是最好的.
如此一来,这个INTERNET_BUFFERS成了鸡肋了.
另外,使用InternetWriteFile传输大文件的时候传说中还要在HttpOpenRequest中加INTERNET_FLAG_KEEP_CONNECTION标志,如果是异步的话应该是非加不可.
继续研究,同时希望LZ有进一步结果能更新上来.
发表于 2011-3-13 01:59:46 | 显示全部楼层
本帖最后由 ceoguang 于 2011-3-13 02:02 编辑
如果想分步向服务器传输请求,只能使用HttpSendRequestEx发送数据。

HttpSendRequest也支持分步发送的.
之前做winsocket发送邮件的时候了解过mime,在http的mime中也有个multipart,可以使用分割符来进行设置.
 楼主| 发表于 2011-3-13 07:47:29 | 显示全部楼层
回复 9# ceoguang

这么晚都不睡?

这里所说的分步跟你理解得有误。你说的是数据结构,而且这不是分步,是分隔,待会我给出代码,你会明白的了。

对照一下两者,HttpSendRequest

HttpSendRequestEx。


两者比较大的区别是,InternetWriteFile需要后者才能使用,而他才是分步发送的关键。

这里以Winhttp为例,我想做的,其实就是这个。
Func _WinHTTP_POSTFile($hOpen,$ServerHost,$URLObj,$FileArray,$ReturnMode=0,$Cookies = "",$MoreHeader = "")
        If $hOpen = "" Or $ServerHost = "" Then Return SetError(1,0,-1)
        $hConnect=_WinHttpConnect($hOpen, $ServerHost)
        ;建立连接
        $hRequest = _WinHttpOpenRequest($hConnect, "POST", $URLObj)
        If @error Or $hRequest = 0  Then Return SetError(1,0,-1) 
        
        $POSTDateLen = 0
        $POSTBasicLen=BinaryLen(StringToBinary(@CRLF&'-----------------------------012345678901'&@CRLF & _
                                                        'Content-Disposition: form-data; name="fileupload"; filename=""'&@CRLF & _
                                                        'Content-Type: application/octet-stream'&@CRLF&@CRLF))
        For $i = 1 To 10
                $POSTDateLen += $POSTBasicLen + BinaryLen(String($i))
        Next
        $POSTDateLen +=47
        ;基本长度获取完毕
        For $i = 1 To UBound($FileArray)
                $POSTDateLen +=FileGetSize($FileArray[$i -1 ]) + BinaryLen(StringToBinary($FileArray[$i -1]))                        ;添加文件及文件路径长度
        Next
        
        $POSTBoundary=_IntBoundary()
        
        _WinHttpAddRequestHeaders($hRequest, "Content-Length: "&$POSTDateLen&@CRLF)
        _WinHttpAddRequestHeaders($hRequest, "Content-Type: multipart/form-data; boundary=---------------------------"&$POSTBoundary)
        
        _WinHttpSendRequest($hRequest)                ;发送请求
        ;--------------------------------发送数据----------------------------------------------
        For $i=1 To 10
                If $i <=UBound($FileArray) Then
                        ConsoleWrite($i&@CRLF)
                        $ContextDate =StringToBinary(@CRLF & '-----------------------------'&$POSTBoundary&@CRLF & _
                                                                                'Content-Disposition: form-data; name="fileupload'&$I&'"; filename="'&$FileArray[$i -1]&'"'&@CRLF & _
                                                                                'Content-Type: application/octet-stream'&@CRLF&@CRLF)
                        _WinHttpWriteData($hRequest,$ContextDate,1)
                        
                        $hFile = FileOpen($FileArray[$i -1],16)
                        While 1
                                $dFile = FileRead($hFile,1024 * 512)
                                If @error Then ExitLoop
                                _WinHttpWriteData($hRequest,$dFile,1)
                                Sleep(10)
                        WEnd
                        FileClose($hFile)
                Else
                        $ContextDate =StringToBinary(@CRLF & '-----------------------------'&$POSTBoundary&@CRLF & _
                                                                                'Content-Disposition: form-data; name="fileupload'&$I&'"; filename=""'&@CRLF & _
                                                                                'Content-Type: application/octet-stream'&@CRLF&@CRLF)
                        _WinHttpWriteData($hRequest,$ContextDate,1)
                EndIf
        Next
        
        $ContextDate =StringToBinary(@CRLF &'-----------------------------'&$POSTBoundary&'--'&@CRLF)
        _WinHttpWriteData($hRequest,$ContextDate,1)
        ;---------------------------------------------- 发送结束
        
        _WinHttpReceiveResponse($hRequest)                ;获取回应
        
        If _WinHttpQueryDataAvailable($hRequest) Then
                MsgBox(0,"OK!",_WinHttpReadData($hRequest,1024*1024*1024))
        Else
                MsgBox(0,"Sorry!","Fault!")
        EndIf
        
        _WinHttpCloseHandle($hRequest)
        _WinHttpCloseHandle($hConnect)
EndFunc


这里,发送请求后,一步步的使用_WinHttpWriteData来完成整个POST过程。小文件的话看不出差别,但对于大文件,可以降低内存占用,也可以加快速度,也可以实时的获取进度。

虽然说现在1楼的代码也可以这么做了,但我不想使用残缺的 INTERNET_BUFFERS 结构,除非证明没有别的方法。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?加入

×
发表于 2011-3-13 12:12:06 | 显示全部楼层
sorry,我理解成多类型了.
实在水平有限,无论怎样填充INTERNET_BUFFERS,都没看到填充的数据包被发送出去.建议去官方问问.
不过这个结构的功能,完全可以由HttpAddRequestHeaders及InternetWriteFile来完成.
另外,这些操作如果换成winsocket来写的话实在简单多了.
 楼主| 发表于 2011-3-13 13:04:24 | 显示全部楼层
本帖最后由 republican 于 2011-3-13 13:11 编辑

回复 11# ceoguang

不过AU3的TCP问题没解决的话,超时问题都很烦。

而且,如果不能多线程,我还是不想用socket。
发表于 2011-3-13 16:20:24 | 显示全部楼层
TCP的问题早前向p版请教过,可以使用异步(非阻塞模式),解决了你所说的超时问题.只是我一直都没整理,所以就没发过上来.
 楼主| 发表于 2011-3-13 16:48:26 | 显示全部楼层
回复 13# ceoguang

等这个等很久了,期待~~~
发表于 2011-3-14 08:39:23 | 显示全部楼层
回复 1# republican
水平有限,无法参与其中,先收藏了!
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-11-15 11:47 , Processed in 0.088991 second(s), 24 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表