HttpSendRequestEx 中如何创建及填充 INTERNET_BUFFERS 结构
本帖最后由 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"
; ====请先填写账号密码
$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 = ;BYTE DataBuffer['&BinaryLen($Context)&']') ]
DllStructSetData($tINTERNET_BUFFERS, "Header", DllStructGetPtr($tINTERNET_BUFFERS, 'HeaderChar'))
DllStructSetData($tINTERNET_BUFFERS, "Buffer", DllStructGetPtr($tINTERNET_BUFFERS, 'DataBuffer'))
DllStructSetData($tINTERNET_BUFFERS, "HeadersLength", StringLen($Header))
DllStructSetData($tINTERNET_BUFFERS, "BufferLength", BinaryLen($Context))
DllStructSetData($tINTERNET_BUFFERS, "HeaderChar", $Header)
DllStructSetData($tINTERNET_BUFFERS, "DataBuffer", $Context)
DllStructSetData($tINTERNET_BUFFERS, "StructSize", DllStructGetSize($tINTERNET_BUFFERS))
; ---End Of StructCreate
_WinINet_HttpSendRequestEx($hRequest, $tINTERNET_BUFFERS)
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&']',DllStructGetPtr($HeaderArray))
$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)
EndFunc ;==>_WinAPI_GetLastError dword StructSize; ptr Next; ptr Header; dword HeadersLength; dword HeadersTotal; ptr Buffer; dword BufferLength; dword BufferTotal; dword Offset这样是不是容易理解些? 回复 2# ceoguang
其实问题是我对这个不是很了解,哪些该填充哪些数据不是很明白。
再说,INTERNET_BUFFERS 有一个 Buffer 结构的指针,此时不是可以直接填充数据么,那么_WinINet_InternetWriteFile又要写什么数据? 我是这样理解的
dwStructSize
下一个结构的大小
Next
下一个结构的指针地址
lpcszHeader
http头(指针)
dwHeadersLength
http头的大小
Buffer
缓冲区指针
BufferLength
缓冲区大小
dwBufferTotal
要发送的总数据大小
后面两个参数为保留值 回复 4# ceoguang
嗯,官方是这么解释的。
1. 首先有个问题,Headerlength 与 HeaderTotal 的区别。
2. header 与 buffer 的数据填充是否像我代码理解的那样操作。
3. structSize 不清楚要怎么计算。 1 哪里来的HeaderTotal?
2 你代码呢?
3 DllStructGetSize
另外,你更新的1#的后一个代码想要怎样修改? 本帖最后由 republican 于 2011-3-12 08:47 编辑
回复 6# ceoguang
现在更新了一楼的代码, 希望你能重新看看,谢谢。 本帖最后由 ceoguang 于 2011-3-12 15:12 编辑
回复ceoguang
现在更新了一楼的代码, 希望你能重新看看,谢谢。
republican 发表于 2011-3-12 08:37 http://www.autoitx.com/images/common/back.gif
很抱歉没细看
另外我在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有进一步结果能更新上来. 本帖最后由 ceoguang 于 2011-3-13 02:02 编辑
如果想分步向服务器传输请求,只能使用HttpSendRequestEx发送数据。
http://www.autoitx.com/images/common/back.gif
HttpSendRequest也支持分步发送的.
之前做winsocket发送邮件的时候了解过mime,在http的mime中也有个multipart,可以使用分割符来进行设置. 回复 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 = 0Then 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 结构,除非证明没有别的方法。 sorry,我理解成多类型了.
实在水平有限,无论怎样填充INTERNET_BUFFERS,都没看到填充的数据包被发送出去.建议去官方问问.
不过这个结构的功能,完全可以由HttpAddRequestHeaders及InternetWriteFile来完成.
另外,这些操作如果换成winsocket来写的话实在简单多了. 本帖最后由 republican 于 2011-3-13 13:11 编辑
回复 11# ceoguang
不过AU3的TCP问题没解决的话,超时问题都很烦。
而且,如果不能多线程,我还是不想用socket。 TCP的问题早前向p版请教过,可以使用异步(非阻塞模式),解决了你所说的超时问题.只是我一直都没整理,所以就没发过上来. 回复 13# ceoguang
等这个等很久了,期待~~~ 回复 1# republican
水平有限,无法参与其中,先收藏了!
页:
[1]
2