首先你要明白下载文件的过程机制。我这几天正在写WINSOCK,把我的经验告诉你吧。
1、发送HTTP头:
首先构建最简请求字符串:
dim HTTPHeader as string
HTTPHeader=""
HTTPHeader=HTTPHeader & "GET /images/smilies/wizenflower.gif HTTP/1.1" & vbCrLf
HTTPHeader=HTTPHeader & "Host: www.vbgood.com" & vbCrLf & vbCrLf
这样的头就可以了。
然后发送请求:
socks.Connect "www.vbgood.com", "80"
然后在connet事件时:
socks.SendData HTTPHeader
2、接收数据
这里是关键。服务器返回的数据有2部分组成。一部分是HTTP响应消息头,另一部分是你要下载的文件二进制数据。服务返回的数据不一定是几次,有可能一次,有可能二次,或者N次,而且响应消息头和返回的二进制数据不一定是分开的,有可能是一起返回,有可能第一次返回消息头,第二次返回二进制数据。服务器不同二不同。而且每次返回的数据大小最大是64KB,这是TCP包的最大尺寸。
你接收不到文件的问题就是出在你只接在ARRIVAL事件收一次数据,这当然是不全的。你应该首先在响应头中查找content_lenth,确定返回的数据总大小。然后定义一个全程byte()字节数组变量,分多次接收返回的数据,用ubound(byte)判断是否已经接收完全。大概思路就是这样,废话不多说,给你我的代码,我已经测试通过。
我写的是支持多线程(多winsock同时下载的代码),不懂的问。
调用方法:在data_Arrival()中
Dim DataByte() As Byte '用字节数组接受数据
socks.GetData DataByte
If SaveData(DataByte(), "wizenflower.gif", 0) = False Then '调用一个winsock(0)
...
End If
If SaveData(DataByte(), "wizenflower.gif", 1) = False Then '再调用一个winsock(1)
...
End If
完整代码如下:
'这是一个支持从网站以HTTP协议下载的模块,支持多线程(WINSOCK)模式。
Private Arrival_First_Times() As Boolean '数据第一次到达
Private F_Num() As Integer '取得系统的空闲文件号
Public File_Lenth() As String '待下载的文件大小
Public Received_Byte() As Long '已接受字节
Public Function SaveData(DataByte() As Byte, FileName As String, Pros_ID As Integer) As Boolean
'DataByte():接收到的字节数组,FileName:要保存的文件名ros_ID:线程ID,支持多WINSOCK。
'根据线程数定义动态数组的大小(保护原有数据模式)
ReDim Preserve Arrival_First_Times(Pros_ID + 1)
ReDim Preserve F_Num(Pros_ID + 1)
ReDim Preserve File_Lenth(Pros_ID + 1)
ReDim Preserve Received_Byte(Pros_ID + 1)
On Error GoTo err
If Arrival_First_Times(Pros_ID) = False Then '数据第一次到达的时候,包含消息头,也可能一起包括2进制文件:所以检查消息头,并分离数据
Dim DataStr As String
DataStr = StrConv(DataByte(), vbUnicode) '这里是将字节数组转化为字符串,以便于操作
If InStr(1, DataStr, "HTTP/1.1 200 OK") Or InStr(1, DataStr, "HTTP/1.0 200 OK") Then '检查http响应状态
'取得待下载的文件大小字节数
Dim pos1, pos2, Title_Lenth, Start_Pos
Title_Lenth = Len("Content-Length:")
pos1 = InStr(1, DataStr, "Content-Length:")
pos2 = InStr(pos1, DataStr, vbCrLf)
File_Lenth(Pros_ID) = Mid(DataStr, pos1 + Title_Lenth, pos2 - pos1 - Title_Lenth)
'从服务器返回的数据中,取得下载文件的起始位置。
'说明:消息结束和文件结束的地方,分别用2个换行表示数据结束。一个换行vbcrlf占用2字节。
'在字符串中,2个换行就是2个vbcrlf;而在字节数组中,就是连续的DataByte(i)=13,DataByte(i+1)=10,DataByte(i+2)=13,DataByte(i+3)=10
For i = 0 To UBound(DataByte()) - 3 '减3是因为文件的最后也是2个换行,而我们需要的是消息头和文件之间的分割,所以不搜索最后的那2次换行。
If DataByte(i) = 13 And DataByte(i + 1) = 10 And DataByte(i + 2) = 13 And DataByte(i + 3) = 10 Then
Start_Pos = i + 4 '加4是因为每个换行占用了2个字节,所以往后4个字节,正好是二进制数据开始的地方
Exit For
End If
Next
'将第一次接受到的字节,除去HTTP响应标题以外的二进制数据,写入文件
If Dir(App.Path & "/" & FileName) <> "" Then '第一次写入文件,如果写入之前文件已存在,则删除。
Kill App.Path & "/" & FileName
End If
F_Num(Pros_ID) = FreeFile()
Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
For i = Start_Pos To UBound(DataByte())
Put #F_Num(Pros_ID), , DataByte(i)
Next
Close #F_Num(Pros_ID)
Arrival_First_Times(Pros_ID) = True '已经完成第一批到达数据的处理。
Else
SaveData = False
Exit Function
End If
Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1 - Start_Pos
Else '如果不是第一到达数据,则直接写入文件
F_Num(Pros_ID) = FreeFile()
Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
If LOF(F_Num(Pros_ID)) > 0 Then Seek #F_Num(Pros_ID), LOF(F_Num(Pros_ID)) + 1
Put #F_Num(Pros_ID), , DataByte()
Close #F_Num(Pros_ID)
Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1
End If
SaveData = True
Exit Function
err:
SaveData = False
End Function
1、发送HTTP头:
首先构建最简请求字符串:
dim HTTPHeader as string
HTTPHeader=""
HTTPHeader=HTTPHeader & "GET /images/smilies/wizenflower.gif HTTP/1.1" & vbCrLf
HTTPHeader=HTTPHeader & "Host: www.vbgood.com" & vbCrLf & vbCrLf
这样的头就可以了。
然后发送请求:
socks.Connect "www.vbgood.com", "80"
然后在connet事件时:
socks.SendData HTTPHeader
2、接收数据
这里是关键。服务器返回的数据有2部分组成。一部分是HTTP响应消息头,另一部分是你要下载的文件二进制数据。服务返回的数据不一定是几次,有可能一次,有可能二次,或者N次,而且响应消息头和返回的二进制数据不一定是分开的,有可能是一起返回,有可能第一次返回消息头,第二次返回二进制数据。服务器不同二不同。而且每次返回的数据大小最大是64KB,这是TCP包的最大尺寸。
你接收不到文件的问题就是出在你只接在ARRIVAL事件收一次数据,这当然是不全的。你应该首先在响应头中查找content_lenth,确定返回的数据总大小。然后定义一个全程byte()字节数组变量,分多次接收返回的数据,用ubound(byte)判断是否已经接收完全。大概思路就是这样,废话不多说,给你我的代码,我已经测试通过。
我写的是支持多线程(多winsock同时下载的代码),不懂的问。
调用方法:在data_Arrival()中
Dim DataByte() As Byte '用字节数组接受数据
socks.GetData DataByte
If SaveData(DataByte(), "wizenflower.gif", 0) = False Then '调用一个winsock(0)
...
End If
If SaveData(DataByte(), "wizenflower.gif", 1) = False Then '再调用一个winsock(1)
...
End If
完整代码如下:
'这是一个支持从网站以HTTP协议下载的模块,支持多线程(WINSOCK)模式。
Private Arrival_First_Times() As Boolean '数据第一次到达
Private F_Num() As Integer '取得系统的空闲文件号
Public File_Lenth() As String '待下载的文件大小
Public Received_Byte() As Long '已接受字节
Public Function SaveData(DataByte() As Byte, FileName As String, Pros_ID As Integer) As Boolean
'DataByte():接收到的字节数组,FileName:要保存的文件名ros_ID:线程ID,支持多WINSOCK。
'根据线程数定义动态数组的大小(保护原有数据模式)
ReDim Preserve Arrival_First_Times(Pros_ID + 1)
ReDim Preserve F_Num(Pros_ID + 1)
ReDim Preserve File_Lenth(Pros_ID + 1)
ReDim Preserve Received_Byte(Pros_ID + 1)
On Error GoTo err
If Arrival_First_Times(Pros_ID) = False Then '数据第一次到达的时候,包含消息头,也可能一起包括2进制文件:所以检查消息头,并分离数据
Dim DataStr As String
DataStr = StrConv(DataByte(), vbUnicode) '这里是将字节数组转化为字符串,以便于操作
If InStr(1, DataStr, "HTTP/1.1 200 OK") Or InStr(1, DataStr, "HTTP/1.0 200 OK") Then '检查http响应状态
'取得待下载的文件大小字节数
Dim pos1, pos2, Title_Lenth, Start_Pos
Title_Lenth = Len("Content-Length:")
pos1 = InStr(1, DataStr, "Content-Length:")
pos2 = InStr(pos1, DataStr, vbCrLf)
File_Lenth(Pros_ID) = Mid(DataStr, pos1 + Title_Lenth, pos2 - pos1 - Title_Lenth)
'从服务器返回的数据中,取得下载文件的起始位置。
'说明:消息结束和文件结束的地方,分别用2个换行表示数据结束。一个换行vbcrlf占用2字节。
'在字符串中,2个换行就是2个vbcrlf;而在字节数组中,就是连续的DataByte(i)=13,DataByte(i+1)=10,DataByte(i+2)=13,DataByte(i+3)=10
For i = 0 To UBound(DataByte()) - 3 '减3是因为文件的最后也是2个换行,而我们需要的是消息头和文件之间的分割,所以不搜索最后的那2次换行。
If DataByte(i) = 13 And DataByte(i + 1) = 10 And DataByte(i + 2) = 13 And DataByte(i + 3) = 10 Then
Start_Pos = i + 4 '加4是因为每个换行占用了2个字节,所以往后4个字节,正好是二进制数据开始的地方
Exit For
End If
Next
'将第一次接受到的字节,除去HTTP响应标题以外的二进制数据,写入文件
If Dir(App.Path & "/" & FileName) <> "" Then '第一次写入文件,如果写入之前文件已存在,则删除。
Kill App.Path & "/" & FileName
End If
F_Num(Pros_ID) = FreeFile()
Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
For i = Start_Pos To UBound(DataByte())
Put #F_Num(Pros_ID), , DataByte(i)
Next
Close #F_Num(Pros_ID)
Arrival_First_Times(Pros_ID) = True '已经完成第一批到达数据的处理。
Else
SaveData = False
Exit Function
End If
Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1 - Start_Pos
Else '如果不是第一到达数据,则直接写入文件
F_Num(Pros_ID) = FreeFile()
Open App.Path & "/" & FileName For Binary Lock Write As #F_Num(Pros_ID)
If LOF(F_Num(Pros_ID)) > 0 Then Seek #F_Num(Pros_ID), LOF(F_Num(Pros_ID)) + 1
Put #F_Num(Pros_ID), , DataByte()
Close #F_Num(Pros_ID)
Received_Byte(Pros_ID) = Received_Byte(Pros_ID) + UBound(DataByte()) + 1
End If
SaveData = True
Exit Function
err:
SaveData = False
End Function