admin管理员组

文章数量:1531347

2024年1月24日发(作者:)

VB中使用WinSock控件传送文件

传送文件对于网络编程来说是基本的功能,比如远程控制软件;在编制一个软件时,我从网上下了很多传文件的程序,这些程序提供的传文件功能根本就不能用;传文本还可以,传二进制文件根本就不行;因此,作为一个基本的功能模块,有必要单独介绍一下;

首先,在VB中要传送字符串,你可以这样写:

DimstrDataAsString

strData="Test"

但是如果你传送的二进制文件,你还能用String变量来存放吗 从理论上分析是不行的,我也做了实验,确实是不行的;文件虽然可以传,但是接受的文件和发送的不一样,原因可能是二进制文件里可以有任何"字符",但是不是所有的字符都可以放在String变量里;

除了String类型的变量,VB中其他类型的变量都只有几个字节长,难道一次只能发几个字节吗 那样岂不是要累死机器了其实,情况没有那么悲观,我们完全可以使用数组来解决这个问题,就是使用byte数组;把要传送的文件都读到数组里,然后发送出去;程序如下:

FileName为要传送的文件名,WinS为发送文件的WinSock控件;这是一个发送端的程序;

PublicSubSendFileFileNameAsString,WinSAsWinsock

DimFreeFAsInteger'空闲的文件号

DimLenFileAsLong'文件的长度

DimbytDataAsByte'存放数据的数组

FreeF=FreeFile'获得空闲的文件号

OpenFileNameForBinaryAsFreeFile'打开文件

DoEvents

LenFile=LOFFreeF'获得文件长度

ReDimbytData1ToLenFile'根据文件长度重新定义数组大小

GetFreeF,,bytData'把文件读入到数组里

CloseFreeF'关闭文件

'发送数据

EndSub

接受端的程序如下:

PrivateSubWinsock1_DataArrivalByValbytesTotalAsLong

DimbytDataAsByte

Dimf

f=FreeFile

OpenstrFileNameForBinaryAsf

ReDimbytData1TobytesTotal

Putf,i,bytData

i=i+bytesTotal'保证每次写都是在文件的末尾,i是个全局变量

Closef

EndSub

这里有两个需要注意的地方,ReDimPreservebytData1ToLenFile,下标是从1开始的,如果你写成ReDimbytDataLenFile,下标就是从0开始了,数组就有LenFile+1长了;LenFile=LOFFreeFile中的LOF是获得文件长度的函数,是VB里带的,我见过很多例子用API,或者循环的读直到末尾来获取文件长度,这样都是很麻烦的,使用LOF函数就可以了;

这样的程序,即可以传送文本文件,也可以传送二进制文件;但是你有没有发现这个程序的问题呢 如果我要传送一个50M的文件呢 系统可以为bytData分配50M的内存空间吗

于是笔者拿一个50M的文件做实验吧,接收到的文件和原来的文件不一样,比原来的大;问题出在那呢

首先,根据文件大小重新定义bytData数组的大小本身就有问题,系统是不可能无限制的给数组分配空间的,即使可以,也会造成系统响应变慢;在传50M文件的时候,系统就跟死机了一样;那么怎么解决这个问题呢,一个自然的想法就是把数据分段传送;程序如下:

发送程序,iPos是个全局变量,初始值为0;这个变量保存着当前数据的位置;ConstiMax=65535是每个数据块的大小;

dimiposaslong

ConstiMax=65535

DimFreeFAsInteger'空闲的文件号

DimLenFileAsLong'文件的长度

DimbytDataAsByte'存放数据的数组

FreeF=FreeFile'获得空闲的文件号

OpenFileNameForBinaryAsFreeF'打开文件

DoEvents

LenFile=LOFFreeF'获得文件长度

IfLenFile<=iMaxThen'如果要发送的文件小于数据块大小,直接发送

ReDimbytData1ToLenFile'根据文件长度重新定义数组大小

GetFreeF,,bytData'把文件读入到数组里

CloseFreeF'关闭文件

'发送数据

ExitSub

EndIf

'文件大于数据块大小,进行分块发送

DoUntiliPos>=LenFile-iMax'发送整块数据的循环

ReDimbytData1ToiMax

GetFreeF,iPos+1,bytData

iPos=iPos+iMax'移动iPos,使它指向下来要读的数据

Loop

'这里要注意的是,必须检查文件有没有剩下的数据,如果文件大小正好等于数据块大小的

'整数倍,那么就没有剩下的数据了

ReDimbytData1ToLenFile-iPos'发送剩下的不够一个数据块的数据

GetFreeF,iPos+1,bytData

CloseFreeF

下面是接收端的程序:

PrivateSubWinsock1_DataArrivalByValbytesTotalAsLong

DimbytDataAsByte

DimlLenFileAsLong

Dimf

f=FreeFile

OpenstrFileNameForBinaryAsf'strFileName是文件名

lLenFile=LOFf

ReDimbytData1TobytesTotal

IflLenFile=0Then'lLenFile=0表示是第一次打开文件,这里有个问题,就是'如果如果该文件存在的话,就会出错,应该在打开前检查文件是否存在;这里我省略了

Putf,1,bytData

Else

Putf,lLenFile+1,bytData

EndIf

Closef

EndSub

VBSOCKET实现文件传输支持断点续传

OptionExplicit

ConstPACKSIZEAsLong=65536'每包大小为64K

PrivatefilepathAsString

PrivatefilenameAsString

PrivatefilelengthAsLong'存储文件信息

PrivatedataAsByte

PrivatepackAsLong

PrivatesendedDataAsLong'数据缓冲区,文件包数,已传输的数据

PrivatealreadySendAsBoolean

PrivatecmsStrAsString

ConstfileDAsString="D:NMSPluginsource"

PrivateSubcmdConnectClient_Click

=sckTCPProtocol

=

=8080

'连接客户端

EndSub

PrivateSubcmdSendFile_Click

OpenfileDForBinaryAs3

filename=""

filelength=LOF3

Close3

"NMSP_AYUREADY"

EndSub

PrivateSubWinsockSend_Connect

="已与客户端建立连接;"

EndSub

'"发送文件"按钮事件代码:

PrivateSubsendFile

DimiAsInteger

DimjAsLong

DimmAsLong

filepath=fileD

="向客户端传送文件:"&filename&"大小为:"&filelength

'计算需要传输文件的包数

pack=filelength-sendedDataPACKSIZE

Iffilelength-sendedDataModPACKSIZE<>0Then

pack=pack+1

EndIf

Ifpack=0Then

pack=pack+1

EndIf

'传输文件

OpenfilepathForBinaryAs1

Fori=1Topack

'如果只有一包

Ifpack=1Then

"filename="&filename&"|filelength="&filelength&"|send="&sendedData

ReDimdatafilelength-sendedData

'读取数据

Forj=sendedData+1Tofilelength

Get1,j,dataj-sendedData

Next

'更新已传输文件的数据

sendedData=filelength

'发送文件数据

'如果是最后一包

ElseIfi+1=packThen

'读取最后一包的数据

ReDimdatafilelength-sendedData

Forj=1Tofilelength-sendedData

Get1,sendedData+j,dataj

Next

'发送文件数据

'更新已传输文件的数据

sendedData=filelength

ExitFor

Else

'将文件数据放到数据缓冲区

ReDimdataPACKSIZE

Form=1ToPACKSIZE

Get1,sendedData+m,dataj

Next

'发送文件数据

'更新已传输文件的数据

sendedData=sendedData+PACKSIZE

EndIf

=IntsendedData/filelength100

Next

=IntsendedData/filelength100

Close1

alreadySend=False

EndSub

'客户端反馈

PrivateSubWinsockSend_DataArrivalByValbytesTotalAsLong

DimcmdStrAsString

,vbString

IfMidcmdStr,1,13="NMSP_IAMREADY"Then'客户端已准备好接收时,要求客户端报告已经接收的文件大小

"NMSP_RPTCURLE"&filename

ElseIfMidcmdStr,1,13="NMSP_REQFILEN"Then'客户端要求发送文件名称

"NMSP_FILENAME="&filename

ElseIfMidcmdStr,1,13="NMSP_REQFILES"Then'客户端要求发送文件大小

"NMSP_FILESIZE="&filelength',vbLong

ElseIfMidcmdStr,1,13="NMSP_RECEIVED"Then'收到客户端已经接收到的文件大小报告

"客户端已经接收了"&MidcmdStr,14,LencmdStr

sendedData=MidcmdStr,15,LencmdStr

Iffilelength=sendedDataThen

"NMSP_SENDDONE"

'初始化文件名,大小,已接收大小,遍历是否还需要向别的客户端发送

Letfilename=""

Letfilelength=0

LetsendedData=0

="文件发送完毕"

Else

CallsendFile

EndIf

EndIf

EndSub

'==========================================================客户端======================================================

OptionExplicit

DimflagAsBoolean'设置是否继续接收文件的开关标识

PrivatereadyReceiveAsBoolean

PrivatefilenameAsString

PrivatetempfileAsString

PrivaterealfileAsString

PrivatereveivePathAsString

PrivatefilelengthAsLong'存储文件信息

PrivatedataAsByte,receivedAsLong'声明数据缓冲区和已接收的数据

PrivateSubForm_Load

reveivePath=&"received"

=sckTCPProtocol

=8080

CallinitReceiveState

EndSub

PrivateSubWinsockReceive_Connect

="已经连接到服务器"

EndSub

PrivateSubWinsockReceive_ConnectionRequestByValrequestIDAsLong

<>0Then

EndIf

="已接受连接请求;"

EndSub

PrivateSubWinsockReceive_DataArrivalByValbytesTotalAsLong

DimjAsLong

'分别接收传输文件的文件名、文件长度

',vbString,bytesTotal-4

',vbLong

'判断指令类型

IfreadyReceive=FalseThen

DimcmdStrAsString

,vbString

IfMidcmdStr,1,13="NMSP_AYUREADY"Then'询问是否准备好接收文件

Iffilename=""Then

"NMSP_REQFILEN"

ElseIffilelength=0Then

"NMSP_REQFILES"

Else

"NMSP_IAMREADY"

EndIf

ElseIfMidcmdStr,1,13="NMSP_FILENAME"Then'文件名

filename=MidcmdStr,15,LencmdStr

"NMSP_REQFILES"

ElseIfMidcmdStr,1,13="NMSP_FILESIZE"Then'文件大小

filelength=MidcmdStr,15,LencmdStr

Iffilelength<>0Then

"NMSP_IAMREADY"

EndIf

ElseIfMidcmdStr,1,13="NMSP_RPTCURLE"Then'服务器端要求提供已经接收的文件大小

'为传输文件设置临时文件

realfile=reveivePath&filename

tempfile=reveivePath&filename&".td"

'返回已接收的数据

'OpenrealfileForBinaryAs1

OpentempfileForBinaryAs2

IfLOF2>0Then

Input2,received

"received="&received

EndIf

Close2

"NMSP_RECEIVED="&received

readyReceive=True

="准备接收文件:"&filename&"大小为:"&filelength

ElseIfMidcmdStr,1,14="NMSP_SENDDONE="Then

'服务器发送文件完毕执行安装操作

EndIf

Else

realfile=reveivePath&filename

tempfile=reveivePath&filename&".td"

="正在接收文件:"&filename&"保存到:"&realfile

'返回已接收的数据

OpenrealfileForBinaryAs1

OpentempfileForBinaryAs2

'建立数据缓冲区

ReDimdatabytesTotal

'接收服务器端传输的数据

,vbArray+vbByte

'将接收的数据写入文件

Forj=received+1Toreceived+bytesTotal

Put1,j,dataj-received-1

Put2,j,dataj-received-1

Next

'更新已接收的数据

received=received+bytesTotal

'更新临时文件

'Write2,received

=Intreceived/filelength100

'传输完毕

>=100Then

="数据传输完毕"

Close2

'删除临时文件

Killtempfile

Close1

CallinitReceiveState

EndIf

Close1

Close2

EndIf

EndSub

PrivateSubinitReceiveState

readyReceive=False

received=0

filelength=0

filename=""

="监听中"

EndSub

本文标签: 文件数据接收发送数组