# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  Visual Basic - Send File With Winsock

## DigiRev

Just a small code snippet to transfer the contents of a file over a Winsock connection (the socket must first be connected). Here is a brief description of the parameters:

SendFile(SocketObject As Variant, FilePath As String, PacketSize As Long)

*SocketObject* - The winsock control you will be using to send the file. It is passed as a variant so you can use a winsock control that is part of an array, or one that isn't.*FilePath* - The full path to the file you are sending.*PacketSize* - The amount of data to send at once, or in one packet. The file is sent in seperate chunks, and this specifies how many bytes to send at a time. For slower connections, like 56k, you will want to make this a small number, like 512 (1/2 KB) or 1024 (1 KB). For high speed internet connections you can make it much larger.



```
Public Sub SendFile(SocketObject As Variant, ByVal FilePath As String, ByVal PacketSize As Long)
Dim lonFF As Long, bytData() As Byte
Dim lonCurByte As Long, lonSize As Long
Dim lonPrevSize As Long

On Error GoTo ErrorHandler

lonSize = FileLen(FilePath)

Open FilePath For Binary Access Read As #lonFF
    
    ReDim bytData(1 To PacketSize) As Byte
    
    Do Until (lonSize - lonCurByte) < PacketSize
        Get #lonFF, lonCurByte + 1, bytData()
        lonCurByte = lonCurByte + PacketSize
        SocketObject.senddata bytData()
        DoEvents
    Loop
    
    lonPrevSize = lonSize - lonCurByte
    
    If lonPrevSize > 0 Then
        ReDim bytData(1 To lonPrevSize) As Byte
        Get #lonFF, lonCurByte + 1, bytData()
        lonCurByte = lonCurByte + lonPrevSize
        SocketObject.senddata bytData()
    End If

Close #lonFF
Exit Sub

ErrorHandler:
    Debug.Print "Send File Error"
    Debug.Print "---------------"
    Debug.Print "Number:      " & Err.Number
    Debug.Print "Description: " & Err.Description
    Debug.Print "File:        " & FilePath
    
End Sub
```

----------


## CVMichael

That code will work only for small files.
Since you are not waiting until you receive the "SendComplete" event from winsock, the winsock internal buffer will overflow after a while. I'm not sure how big the winsock buffer is though.
I am writing this ONLY because I don't want people who don't know anything about winsock, to learn the wrong way to do this.
The right way to transfer a file of any size, is to wait for the "SendComplete" event from winsock, then send another chunk. You will also have to use a timer, because you can't call the Winsock.SendData sub from the SendComplete event.

Anyways... beginners don't use this code ! only if you know what you are doing, then use it...

----------


## CVMichael

THE RIGHT WAY TO DO IT !

I did not actually test this, but it should work...

VB Code:
Option Explicit
 Private iFileNum As Integer, lPacketSize As Long
 Public Sub SendFile(SocketObject As Winsock, ByVal FilePath As String, Optional ByVal PacketSize As Long = 1024)
    Dim Buffer() As Byte
    
    lPacketSize = PacketSize ' save the PacketSize for the timer
    Timer1.Enabled = False ' make suze timer is not enabled
    
    iFileNum = FreeFile ' get free file number
    Open FilePath For Binary Access Read As iFileNum ' open file
    
    ' if file size is smaller than PacketSize, then send the whole file, but not more
    ReDim Buffer(lngMIN(LOF(iFileNum), PacketSize) - 1)
    Get iFileNum, , Buffer ' read data
    
    SocketObject.SendData Buffer ' send data
End Sub
 Public Function lngMIN(ByVal L1 As Long, ByVal L2 As Long) As Long
    If L1 < L2 Then
        lngMIN = L1
    Else
        lngMIN = L2
    End If
End Function
 Private Sub Timer1_Timer()
    Dim Buffer() As Byte, BuffSize As Long
    
    Timer1.Enabled = False
    If iFileNum <= 0 Then Exit Sub
    
    If Loc(iFileNum) >= LOF(iFileNum) Then ' FILE COMPLETE
        Close iFileNum ' close file
        iFileNum = 0 ' set file number to 0, timer will exit if another timer event
        
        Exit Sub
    End If
    
    'if the remaining size in the file is smaller then PacketSize, the read only whatever is left
    BuffSize = lngMIN(LOF(iFileNum) - Loc(iFileNum), lPacketSize)
    
    ReDim Buffer(BuffSize - 1) ' resize buffer
    Get iFileNum, , Buffer ' read data
    SocketObject.SendData Buffer ' send data
    
    ' Show progress
    Me.Caption = Format(Loc(iFileNum) / CDbl(LOF(iFileNum)) * 100#, "#0.00") & "% Done"
    
    ' timer event will be called again when last packet is sent, close the file then
End Sub
 Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    ' read incomming data here...
End Sub
 Private Sub Winsock1_SendComplete()
    ' can't call SendData here, so enable timer to do it...
    Timer1.Enabled = False
    Timer1.Interval = 1
    Timer1.Enabled = True
End Sub

----------


## DigiRev

Sorry, but the TCP buffer will not overflow using my code.

Sending only 1024 bytes at a time makes it impossible for the buffer to overflow, even on slower connections considering the TCP buffer is 32768 bytes in size (over 32 MB) no connection can even transmit enough data to make it overflow, even over a LAN...

No offense, but using a timer is one of the worst ways to go about it. If you're going to wait for the SendComplete() event to fire, you should use a boolean flag variable and a do/until loop until the data has been trasmitted...ie:

SendData bytBuffer()
bolSending = True

Do
    DoEvents
Loop Until bolSending = False

.....

Sub Winsock_SendComplete()
bolSending = False
'Transer will continue from this point.

----------


## souren

The sendcomplete event does not guarantee that the data has been received by the destination, only that it has been sent by the sending app

----------


## CVMichael

> The sendcomplete event does not guarantee that the data has been received by the destination, only that it has been sent by the sending app


Ammm... yes it does... or are you talking about UDP ?

----------


## mu_ds

> Sorry, but the TCP buffer will not overflow using my code.
> 
> Sending only 1024 bytes at a time makes it impossible for the buffer to overflow, even on slower connections considering the TCP buffer is 32768 bytes in size (over 32 MB) no connection can even transmit enough data to make it overflow, even over a LAN...
> 
> No offense, but using a timer is one of the worst ways to go about it. If you're going to wait for the SendComplete() event to fire, you should use a boolean flag variable and a do/until loop until the data has been trasmitted...ie:
> 
> SendData bytBuffer()
> bolSending = True
> 
> ...


hello, i've come across a similar problem with a multiplayer game im making, it seems the data gets appended when its sent too fast with winsock, i thought this same idea and logic for resolving the issue but i wanted to confirm it. now that im thinking and have seen your code, you have an infinite loop
Do
    DoEvents
Loop Until bolSending = False

if for some reason the sendcomplete is not triggered(maybe a winsock error or disconnect??) then the program would get locked. or everytime you use the send data method its always 100% completed? even if the connection is lost?? even if not connected a few milliseconds before??

well im thinking about using timegettime or a similar system function to specify a timeout in milliseconds, just in case the program would get stuck and then display a timeout sending data msg in the server's log.

the other solution i have seen around is to put another character at the end and split but to me seems like a bad solution to be sending an extra byte for every msg or even more when they append,but somehow i think this would never delay the send, ever..but i hope that making a loop would slow down the least as possible...a couple of ms i think. not more than 10-25 ms hopefully.

----------


## CVMichael

I agree with all the points you've made, and that's why I said that that code is not good.

It is much safer to use a timer the way I showed in post #3

----------


## mu_ds

> I agree with all the points you've made, and that's why I said that that code is not good.
> 
> It is much safer to use a timer the way I showed in post #3


while your code is safe, and good for an app and for sending files in this case for a multiplayer game and sending just game data i believe is not good to use timer controls. because i would have to load a new timer control instance wich will quickly drain a lot of memory from the server and they are not fast like system timer functions. in my case its more simple because i just want to send normal data. i believe i have to specify in the server program a timeout variable, and i would type in the desired milliseconds to wait for the sendcomplete and it would not get stuck or have to load a timer control wich is complicated and not so professional for a game.

i unless someone can tell me im wrong and its not necessary to worry about the sendcomplete method failing.

another thing im thinking is if to store the confirmation of the sending in an dynamic array of booleans or just to use the tag property of the winsocks array for that. wich is faster? any suggestions on that?

----------


## DigiRev

This code wasn't intended as a function to solve all of your file-transfer needs. It was just an example of how to transfer the contents of a file over a winsock connection. Of course you will have to modify a few things to suit whatever it is you're doing.

You may want to add a timeout procedure incase the socket gets disconnected, or encounters an error. ie:

Do
DoEvents
Loop Until Sending = False Or TimeOut = True

If you want to use a timer to send the file, it will work but it isn't the best way to go about it. The timer tells you nothing about wether or not the data was sent completely or not, the SendComplete() and SendProgress() events do (that's what they're intended for  :Wink: )

Anyway, I've started on a file sender/receiver ActiveX control so I'll post that here when it's finished. It will be much more reliable than this code.

----------


## DigiRev

> i unless someone can tell me im wrong and its not necessary to worry about the sendcomplete method failing.


I've never had the SendComplete() event fail. If it does, it's because there was a socket error, which means the Socket_Error() event will fire, in which case, you should use for error handling.




> another thing im thinking is if to store the confirmation of the sending in an dynamic array of booleans or just to use the tag property of the winsocks array for that. wich is faster? any suggestions on that?


If you're using multiple sockets in an array, you could use the .Tag property, but it would be faster/more effecient to use a boolean array. Although the speed difference probably wouldn't be noticeable.

----------


## jack1234

Hi CVMicheal,

For your post at 04-02-2005, 11:31 PM, code line 15
it wrote
ReDim Buffer(lngMIN(LOF(iFileNum), PacketSize) - 1)

Is it more correct if we put 
ReDim Buffer(lngMIN(LOF(iFileNum)-LOC(iFileNum), PacketSize) - 1)

?

----------


## CVMichael

I don't see why to put the Loc(iFileNum) when you just opened the file, and Loc(iFileNum) is always at the beginning of the file...

Loc(iFileNum) returns the cursor location
Lof(iFileNum) returns the file size

At line 43 is needed because we already read some data from the file...

----------


## jack1234

Hi CVMichael, you are right. Thanks :Smilie:  The crucial line is at line 43.

I missed the timer part because I implement it as a do while loop with a boolean flag which was mentioned by DigiRev, but referencing your send complete methodology. This is due to I do this in the vba form, and it only has 1 general timer, and I have used it for other purposes.
Hence for my case, it is important to set the boolean flag(to the value that will make it go out of busy waiting in the while...wend loop) in any potential ending places and within the error handler.

Thanks for the posts, it is very helpful :Smilie:

----------


## canadadoya

hi, in this code does it send the contents of the file or the file itself? Im making a program to login using the file contents, reading it and sending the info with sck.SendData.

----------


## DigiRev

> hi, in this code does it send the contents of the file or the file itself? Im making a program to login using the file contents, reading it and sending the info with sck.SendData.


It sends the contents of the file. The receiving end will need to receive the data and dump it into the file.

I'm working on a file transfer control for sending multiple files at once complete with MD5 hashing to check for corruption. Should be done soon.

In the meantime, I would recommend you use CVMichael's example which is also in the CodeBank.

This was just a code snippet for how to read a file in chunks and basically just send it one chunk at a time (definitely not how I do it anymore  :Wink: ).

But it will work.

----------


## yan870126

Dear Professionals,
    I need some help here. I'm a new VB beginner, i'm trying to do something like sending files(m2p,jpeg,avi,ppt,txt...) some of the video file might more over than 100mb and i'll develop it by LAN use 1st. any idea for this? 
thanks

----------


## mike1960

I have a question about sending files with a socket and error checking.  I'm actually using the old MABRY Socket control and I have a question about error checking when sending a packet.  In general, I have my file transfer working but it is only about half as fast as the maximum transfer rate which I see when sending files with FTP.

I notice that the larger the packets I send the faster it goes. However, I have also noticed that the larger the packet (even only those above 3K sometimes the error rate seems to go up).   If I increase it from about 1024 bytes to around 8K it goes faster but for some reason there are errors and I have to resend the packet.  So, here's my question and I have a feeling I'm doing this all wrong but here goes:

1.  The Client sends an 8K packet to the server along with a 2 byte checksum attached to the packet that my client program generates.
2.  The server gets the 8K packet and validates it with the checksum
3.  If it's bad the server then sends about 3 bytes or so back to the client telling it to resend the packet.  This continues until all the packets are successfully sent.

Some how I have a feeling I'm just not doing this right.  If the server has to wait for the full 8K to arrive then it would seem that during that time the client could be sending another packet and continue sending and sending packets.  However, what happens if the packet reaches the server and the data is not what was sent and there is a glitch in one or two or more of the bytes in the packet?

One other question - with Mabry Socket there's something called Fast Notifications and I'm not sure if this is useful or important to utilize.

Anyone use the Mabry socket control out there?

I'm confused.
Please help.
Thanks,

----------


## Inuya5ha

For some reason, even when waiting for the *SendComplete* event, files are transferred enterily only over LAN, and not the Internet. When accesing the file via the Internet IP, _only the first block of data arrives to destination_, no matter how I modify the code... what could be the reason for this?

----------


## mike1960

I got everything figured out before - it was because VFP 6 does not properly handle the ActiveX control and I have found a workaround.

Inuya5ha - it sounds like the reason you are having trouble is because you cannot just look for data from the socket in a tight loop.  You have to wait for the event to fire so that you know data has arrived and then grab it.  The SendComplete event will fire again if you wait long enough then more data should arrive.

Mike

----------


## Inuya5ha

> Inuya5ha - it sounds like the reason you are having trouble is because you cannot just look for data from the socket in a tight loop.  You have to wait for the event to fire so that you know data has arrived and then grab it.  The SendComplete event will fire again if you wait long enough then more data should arrive.


Thanks, however I don't handle the receiving of the file, that is done by the users' browser (Firefox or IE). So it definitely must be an error in the code posted here, also notice that it works OK over LAN... should I delay the process somewhere to ensure data is properly sent to the browser?

----------


## KJKO

How we do on client side ?

----------

