Solved

Winsock Help needed....... (Part 2)

Posted on 2004-09-02
14
1,528 Views
Last Modified: 2013-11-13
Hi all,
         Please first have a look at this Question ..
http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_21115102.html.
         Idle_Mind has been kind enough to share some of his precious time and code in solving my problem........With his help I was able to connect Multiple clients on one server......Almost all the Authentication and Connection problems are sorted out......
Now I come to my second problem.......I need to send some data from my Server to the Client.......This data can be anywhere between 280 bytes to x*280 bytes where x can be any integer even 0.1 million(as an extreme case).....but usually it will be round 1-500......I load this data into a String Variable......Now I want the following information....

I know that I will need to parse that data into chunks.....I am comfortable with this but what I want to know is the max. limit of bytes that can be send in one chunk without any loss......I know that there might be different suggestions from different people
but I want to know a safe value so that my chunks dont get corrupted....

Will I need to add some delimitir at the end of each chunk so that I am sure that I have received the full chunk at the Client side
so my communication should be like this......(as i found out on some website)

Server: I'm ready to send you a file now!
Client: Ok, go ahead and send me the first chunk.
Server: Here it is!
Client: Thanks, go ahead and send me the next chunk.
Server: Here it is!
Client: Thanks, go ahead and send me the next chunk.
Server: Here it is!
...
Server: There are no more chunks!
Client: That's all the data? Thanks for the file!

Can someone help me in this regard so that the system is compatible to that code which Idle_Mind has suggested me.....
In fact I am very busy in some other part of the project. So that is why I am getting almost no time to code it by myself.....So a working example (that fits in the sample provided by Idle_Mind ) will be greatly appreciated.....

Imran Arshad
0
Comment
Question by:imarshad
  • 6
  • 5
  • 3
14 Comments
 
LVL 7

Expert Comment

by:wesbird
ID: 11966996
There is no one size fits all answer to this.  Since you're using TCP and not UDP the message is logically a continous stream which you are going to chop into arbitrary blocks.  

You can't directly control how TCP will do this with the Winsock control because it does not allow you to allow you so set socket options like e.g. Nagle Algorithm as you can in C/C++.  The Nagle Algorithm is a sort of optimisation tool for TCP because it allows it to buffer up the data into blocks to get as many bytes into a packet so that you don't end up sending a packet for every single byte which would be very inefficient because of protocol overhead.  

TCP has its own recovery mechanisms so corruption should in theory not happen (when you get a corrupt download it is likely not because of TCP, but because of a corrupted file on the web server), but if you want belt and braces then you can always add your own checksum at the end of your blocks.

One thing to consider is how TCP/IP itself manages its own blocks.  The "MTU" defines the maximum size of a single packet, e.g. for ethernet is 1040 bytes and 576 bytes for dialup.  These packets are below the TCP stream level but may be a useful indicator.  You may also want to consider the size of the data blocks that you're transmitting.  What you can do is control the size of what you apply to the .Send method.  If you call Winsock's .Send with a block larger than the MTU, then TCP will slice it up itself - just don't worry about this.

Other things to consider include whether the data arrives in bursts or is instantaneously available.  A way to deal with bursty data do this is to create an 8K or 64K buffer and add messages into the buffer, and then transmit the buffer (less the unused bytes) when there is no space for the next data block, or when some time limit expires.  Think also about how long it will take to transmit each chunk - because that will affect timeouts that you code into the Server to detect dead connections - is this an issue - how long should the server wait before it realises that a client is dead?  One advantage of small frequent blocks is that you're closer able to monitor network congestion more quickly and control the rate at which you send data if you so desire.

0
 
LVL 13

Author Comment

by:imarshad
ID: 11968758
>> What you can do is control the size of what you apply to the .Send method.  If you call Winsock's .Send with a block
>>larger than the MTU, then TCP will slice it up itself - just don't worry about this.

So logically if I send my 200 kB String variable it should cause no problems.....as TCP will send my data in chunks of 576 bytes and at the receiver end I will receive the chunks sequentially.....and since my end of file is followed by "***" then i will know that all data has reached me.....

 I will be sending data from Server as follows

Server.send "###"
server.send MyString   '200 kBytes in this variable
Server.send "***"

Can it be possible that I receive "***" and I may miss some packet from the 200kB file ? or will TCP ensure that I will receive all the data from MyString and then I will receive "***" ?

But still I see people using Packet Delimitir for transfering files via Winsock......

>>Other things to consider include whether the data arrives in bursts or is instantaneously available.  A way to deal with
>>bursty data do this is to create an 8K or 64K buffer and add messages into the buffer, and then transmit the buffer (less
>>the unused bytes) when there is no space for the next data block, or when some time limit expires.

What if I use the following code
Private Sub Winsock1_DataArrival(index As Integer, ByVal bytesTotal As Long)
  Dim inputString As String  
    Winsock1.GetData inputString, vbString
    inputBuffer= inputBuffer& inputString
   call CheckStartEndFile( )
End Sub

Where the CheckStartEndFIle checks for "###" as a start and "***" as the end of file and writes all that is between them into the file......Is this a good approach ? Will it work every time ? Is there some better suggestions ?

or should I use the approach that I have suggested in my Question ? If yes then I need some code to fit into my existing code (from Idle_Mind)......

Imran Arshad
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 11970277
Imran,

You need to understand that there are two different kinds of streams in a file transfer application and that those streams require very different handling.

The first type of stream is known as the control stream which handles all the handshaking.  This typically involves a login validation if there is one and the exchange of header information for the filename(s) and size(s) to be transferred.  It is this stream that requires a persistent buffer since this data makes no sense when it is fragmented.

The second type of stream is known as the data stream which consists of the raw data from the file(s) being transferred.  A persistent buffer is not required for the data stream.  The raw data will arrive in the order it was sent, just possibly not in the same chunk sizes.  This doesn't matter since all you need to do is write whatever has arrived directly into the file as you receive it.  If you exchange the number of bytes that are going to be sent beforehand and then track how many have acutally arrived, you will know when to expect the end of file marker without needing to search for it every time.  Consequently, you don't need to temporarily store the entire data stream until you receive the end of file marker.  Instead, you can leave the file open and write each chunk as it arrives.

It is actually much easier to implement a file transfer using two concurrent connections instead of only one.  The control stream can be handled by one connection while the data stream is handled by the other.  This is actually the way FTP transfers are implemented.  Since only raw file data flows on the second connection, you don't need to worry about finding the begininning or ending markers in the stream.

Idle_Mind

0
 
LVL 13

Author Comment

by:imarshad
ID: 12018893
>>You need to understand that there are two different kinds of streams in a file transfer application and that those streams
>>require very different handling.

>>It is actually much easier to implement a file transfer using two concurrent connections instead of only one.  The control
>>stream can be handled by one connection while the data stream is handled by the other.

But due to my lack of in depth knowledge I have developed my application around the same lines.... i.e I use the same control to authenticate and then Send File in chunks........

I have something like this in the DataArrival Event of Server......

Case "Update"
'Check the "Client Info" and "Password" and then authenticate.....and then tell the Client I am ready to send you data
Case "NEXT"
'Send the first Chunk.......If there is no more chunks then Send EOF Marker.......
'It will be of the form "Chunk & chr(0) & Data & chr(1)"

and in the Data Arrival of Client I have

Case "SendingData"
'Initialize a buffer to receive the data and send "NEXT"
Case "Chunk"
This is a data chunk so store in the buffer allocated.....Now Send "NEXT"
Case "EOF"
Data Completed.....So save the file........

This is working for me now.....I have left quite few details but I think it will be enough to make you understand my algorithm

But according to you it is better to have a Second Winsock control so that after the authentication part is completed on one winsock only Data should be send on the other winsock.........

Can you please tell me why my technique is inferior to yours ? What are the glitches or problems that I will face if I use my technique? though it is working fine right now........


Just a side question have you used wininet.dll?

Imran Arshad
0
 
LVL 7

Expert Comment

by:wesbird
ID: 12019720
I think what  Idle_Mind  may have forgot to mention is that twin stream applications are usually implemented in multithreaded systems where the control thread can send a signal (unblock a Mutex) to the other thread which then responds to the request for more data.  This is great in C/C++ if you're writing a high performance NT service -  but very very difficult (multiple threads that is) in VB.

The wininet.dll handles a lot of async stuff for you in providing the notification events to make writing simple TCP/IP a lot easier in VB.  I personally find that unless performance is a major issue that it's a lot more productive to be able to write something in VB in a tenth of the time that it would take in C++, so I use the control.

It is possible to get great performance with a single stream. The technique of prefixing the block with a "type" marker so that the receiver knows whether it's control or data is well established.

I think that now that you have it sorted out and the performance is OK then you should stick with what you have.  

Cheers,

Wes
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 12020897
>> But according to you it is better to have a Second Winsock control so that after the authentication part is completed on >> one winsock only Data should be send on the other winsock.........

>> Can you please tell me why my technique is inferior to yours ? What are the glitches or problems that I will face if I use >> my technique? though it is working fine right now........

I don't think either method, single or dual winsock controls, is better than the other.  They both have their advantages and disadvantages.

My main point was that you need to handle the two differenct kinds of streams, not connections differently: Control streams need a persistent buffer while data streams do not.

Idle_Mind
0
 
LVL 13

Author Comment

by:imarshad
ID: 12062250
Hi,
          As stated in my earlier post I have developed the algorithm of sending my data over the Winsock......I tried it on Single PC as well as on a Network......It worked quite well and fast enough......But now I am testing it on the Internet and the file transfer is really slow.....I transfered a file of about 967kB and it took more then 1 hr to transfer....To check my internet connection downloading speed I also downloaded a file from Some Website of about the same size and it took about 5 min. to download a 980 kB file......SO this is highly undesireable for me......My technique is as follows....

After authentication and Verification I divide the data to be transfered into small chunks......Each of this chunk will have 10*280 bytes i.e 2800 bytes.... and each chunk is embedded with a terminating character.....When my Client receives the terminating character it requests the next chunk (This makes sure that the earlier chunk is received and then the request for next chunk is issued).....
I have tested on my Client that the next Chunk reaches after variable time......When I issue "Next" command from the client the "Chunk" (2800 bytes) reaches me sometimes in 5 sec. and sometimes in  20 Sec. (really slow)... or even more late then this.....What could be the reason ? Shouldn't the 2800 bytes reach me within 1-2 sec. ?

I have read on the internet that the file should be break into chunks and when each chunk  reaches at the Client then the Next chunk should be sent.......Is this correct?

Also is 2800 bytes a good chunk size ? Should it be smaller then this or larger then this? If I change it to say 5600 bytes then will I face loss of Packets? or not?

Imran Arshad
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 400 total points
ID: 12065933
Imran,

Below is a peer to peer file transfer application I wrote that utilizes two connections similar to an FTP session.  The first connection is used for relaying control and chat messages.  The second connection is used exclusively for transferring the file.

To create this application, paste the code below into notepad and save it as Form2.frm.  Then create a new project and add the newly created form to it.  Remove the default form from the project.  Be sure to go in to Project --> Properties and reset the Startup Object field.  Compile the application and run two instances of the EXE.

Hit the Listen button on one of the instances.  In the other instance, type in the IP shown or the local IP (127.0.0.1) and hit connect.  Once you have a connection, you may chat between the applications.  Now hit the Transfer button.  A File Open dialog box will appear allowing you to select the file to send.  Once the file has been selected, a Save As dialog box will appear for the other application.  If the receiver accepts the file then transfer will begin.  Chatting my continue while the file is being transferred.

This may not be the way you end up doing it, but the application demonstrates the important aspects of reading and writing to a file in chunks with a byte array for sending acrosss a TCP/IP connection.

Regards,

Idle_Mind

VERSION 5.00
Object = "{248DD890-BB45-11CF-9ABC-0080C7E7B78D}#1.0#0"; "MSWINSCK.OCX"
Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0"; "RICHTX32.OCX"
Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0"; "COMDLG32.OCX"
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"
Begin VB.Form Form1
   BorderStyle     =   1  'Fixed Single
   Caption         =   "File Transfer Application"
   ClientHeight    =   4650
   ClientLeft      =   45
   ClientTop       =   330
   ClientWidth     =   5865
   LinkTopic       =   "Form1"
   MaxButton       =   0   'False
   MinButton       =   0   'False
   ScaleHeight     =   4650
   ScaleWidth      =   5865
   StartUpPosition =   3  'Windows Default
   Begin VB.TextBox Text3
      Height          =   285
      Left            =   4440
      TabIndex        =   9
      Text            =   "10001"
      Top             =   240
      Width           =   1335
   End
   Begin VB.TextBox Text2
      Height          =   285
      Left            =   2400
      TabIndex        =   8
      Text            =   "Enter IP to Connect to"
      Top             =   240
      Width           =   1935
   End
   Begin VB.TextBox Text1
      Height          =   375
      Left            =   120
      TabIndex        =   6
      Top             =   4200
      Width           =   5655
   End
   Begin RichTextLib.RichTextBox RichTextBox1
      Height          =   2655
      Left            =   120
      TabIndex        =   5
      TabStop         =   0   'False
      Top             =   1440
      Width           =   5655
      _ExtentX        =   9975
      _ExtentY        =   4683
      _Version        =   393217
      ReadOnly        =   -1  'True
      ScrollBars      =   2
      TextRTF         =   $"Form1.frx":0000
   End
   Begin VB.CommandButton Command4
      Caption         =   "Command4"
      Height          =   375
      Left            =   4440
      TabIndex        =   4
      Top             =   600
      Width           =   1335
   End
   Begin MSComctlLib.ProgressBar ProgressBar1
      Height          =   255
      Left            =   120
      TabIndex        =   3
      Top             =   1080
      Width           =   5655
      _ExtentX        =   9975
      _ExtentY        =   450
      _Version        =   393216
      Appearance      =   1
   End
   Begin VB.CommandButton Command3
      Caption         =   "Command3"
      Height          =   375
      Left            =   3000
      TabIndex        =   2
      Top             =   600
      Width           =   1335
   End
   Begin VB.CommandButton Command2
      Caption         =   "Command2"
      Height          =   375
      Left            =   1560
      TabIndex        =   1
      Top             =   600
      Width           =   1335
   End
   Begin VB.CommandButton Command1
      Caption         =   "Command1"
      Height          =   375
      Left            =   120
      TabIndex        =   0
      Top             =   600
      Width           =   1335
   End
   Begin MSWinsockLib.Winsock Winsock1
      Index           =   0
      Left            =   0
      Top             =   480
      _ExtentX        =   741
      _ExtentY        =   741
      _Version        =   393216
   End
   Begin MSComDlg.CommonDialog CommonDialog1
      Left            =   480
      Top             =   480
      _ExtentX        =   847
      _ExtentY        =   847
      _Version        =   393216
   End
   Begin VB.Label Label3
      Caption         =   "Port #"
      Height          =   255
      Left            =   4560
      TabIndex        =   11
      Top             =   0
      Width           =   1095
   End
   Begin VB.Label Label2
      Caption         =   "IP to Connect to:"
      Height          =   255
      Left            =   2520
      TabIndex        =   10
      Top             =   0
      Width           =   1335
   End
   Begin VB.Label Label1
      Alignment       =   2  'Center
      Caption         =   "My IP is 255.255.255.255"
      Height          =   255
      Left            =   0
      TabIndex        =   7
      Top             =   240
      Width           =   2415
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Private inputBuffer As String
Private fileName As String
Private fileNamePath As String
Private fileSize As Long
Private Const chunk As Integer = 4096
Private ff As Integer
Private resumePoint As Long
Private cancelTransfer As Boolean

Private Sub Form_Load()
    Label1.Caption = "My IP is " & Winsock1(0).LocalIP
    Command1.Caption = "Listen"
    Command2.Caption = "Connect"
    Command3.Caption = "Transfer"
    Command3.Enabled = False
    Command4.Caption = "Cancel Transfer"
    Command4.Enabled = False
    Winsock1(0).RemoteHost = "127.0.0.1"
    Load Winsock1(1)
End Sub

Private Sub Command1_Click()
    On Error GoTo portInUse
   
    Dim portNumber As Long
   
    ' Listen
    If IsNumeric(Text3.Text) Then
        portNumber = CLng(Text3.Text)
        If portNumber < 0 Then
            portNumber = 0
        End If
       
        Winsock1(0).LocalPort = portNumber
        Winsock1(0).Close
        Winsock1(0).Listen
        Text3.Text = Winsock1(0).LocalPort
        Me.Caption = "Listening on Port " & Winsock1(0).LocalPort & "..."
        Command1.Enabled = False
        Command2.Enabled = False
    Else
        MsgBox "Please enter a valid Port number to Listen on."
    End If
    Exit Sub

portInUse:
    If Err.Number = sckAddressInUse Then
        MsgBox "Port " & Winsock1(0).LocalPort & " is already in use"
    Else
        Err.Raise Err.Number
    End If
End Sub

Private Sub Command2_Click()
    ' Connect
    Dim portNumber As Long
   
    If IsNumeric(Text3.Text) Then
        portNumber = CLng(Text3.Text)
        If portNumber > 0 Then
            Command2.Enabled = False
            Command1.Enabled = False
            Me.Caption = "Connecting..."
            Winsock1(0).RemoteHost = Text2.Text
            Winsock1(0).RemotePort = portNumber
            Winsock1(0).LocalPort = 0
            Winsock1(0).Connect
        Else
            MsgBox "Please enter a valid Port Number to connect to."
        End If
    Else
        MsgBox "Please enter a valid Port Number to connect to."
    End If
End Sub

Private Sub Command3_Click()
    ' Transfer
    On Error GoTo cancelled
   
    Dim xFerData As String
   
    Winsock1(0).SendData "p" & Chr(1)
    Command3.Enabled = False
    CommonDialog1.DialogTitle = "Send which File?"
    CommonDialog1.CancelError = True
    CommonDialog1.InitDir = App.Path
    CommonDialog1.ShowOpen
   
    xFerData = CommonDialog1.fileName
    fileNamePath = Left(xFerData, InStrRev(xFerData, "\"))
    fileName = Mid(xFerData, Len(fileNamePath) + 1)
    fileSize = FileLen(fileNamePath & fileName)
    If Winsock1(0).State = sckConnected Then
        Me.Caption = "File Transfer is Pending..."
        xFerData = "r" & Chr(0) & fileName & Chr(0) & fileSize & Chr(1)
        Winsock1(0).SendData xFerData
    End If
    Exit Sub
   
cancelled:
    Winsock1(0).SendData "c" & Chr(1)
    Command3.Enabled = True
End Sub

Private Sub Command4_Click()
    Me.Caption = "Transfer Cancelled"
    cancelTransfer = True
    If Winsock1(0).State = sckConnected Then
        Winsock1(0).SendData "x" & Chr(1)
    End If
    Winsock1_Close 1
End Sub

Private Sub Winsock1_ConnectionRequest(Index As Integer, ByVal requestID As Long)
    Winsock1(Index).Close
    Winsock1(Index).Accept requestID
    If Index = 0 Then
        Me.Caption = "Connected"
        Command3.Enabled = True
    Else
        Me.Caption = "File Transfer connection made..."
        Command4.Enabled = True
    End If
End Sub

Private Sub Winsock1_Connect(Index As Integer)
    Dim fs As Long
    Dim cp As Long
    Dim remaining As Long
    Dim nextChunk() As Byte
   
    If Index = 0 Then
        Me.Caption = "Connected"
        Command3.Enabled = True
    ElseIf Not cancelTransfer Then
        ff = FreeFile
        Open fileNamePath & fileName For Binary Access Read As #ff
        Seek #ff, resumePoint + 1
        fs = LOF(ff)
        cp = Loc(ff)
        remaining = fs - cp
        If remaining < 1 Then
            Winsock1_Close Index
        Else
            Command4.Enabled = True
            If remaining >= chunk Then
                ReDim nextChunk(chunk - 1)
            Else
                ReDim nextChunk(remaining - 1)
            End If
            Get #ff, , nextChunk
            Winsock1(Index).SendData nextChunk
        End If
    End If
End Sub

Private Sub Winsock1_SendComplete(Index As Integer)
    Dim fs As Long
    Dim cp As Long
    Dim remaining As Long
    Dim nextChunk() As Byte
    Dim p As Integer
       
    If Index = 1 And Not cancelTransfer Then
        ' send next chunk of file
        If cancelTransfer Then
            Exit Sub
        End If
        fs = LOF(ff)
        cp = Loc(ff)
        p = CInt(cp / fs * CDbl(100))
        If p >= 0 And p <= 100 Then
            Me.Caption = p & "% Transferred..."
            ProgressBar1.Value = p
            DoEvents
        End If
        remaining = fs - cp
        If remaining < 1 Then
            Winsock1_Close Index
            Me.Caption = "Transfer Complete"
            MsgBox "Transfer Complete"
        Else
            If remaining >= chunk Then
                ReDim nextChunk(chunk - 1)
            Else
                ReDim nextChunk(remaining - 1)
            End If
            If cancelTransfer Then
                Exit Sub
            End If
            Get #ff, , nextChunk
            Winsock1(Index).SendData nextChunk
        End If
    End If
End Sub

Private Sub Winsock1_Close(Index As Integer)
    Dim cp As Long
   
    Winsock1(Index).Close
    If Index = 0 Then
        Me.Caption = "Disconnected"
        Command1.Enabled = True
        Command2.Enabled = True
        Command3.Enabled = False
    Else
        cp = Loc(ff)
        Close
        If cp = fileSize Then
            Me.Caption = "Transfer Complete"
            MsgBox "Transfer Complete"
        End If
        Command3.Enabled = True
        Command4.Enabled = False
    End If
End Sub

Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)
    Dim inputString As String
    Dim binaryData() As Byte
    Dim cp As Long
    Dim p As Integer
    Static lastP As Integer
   
    If Index = 0 Then
        ' add current command to buffer and process the buffer
        Winsock1(Index).GetData inputString, vbString
        inputBuffer = inputBuffer & inputString
        processInputs
    ElseIf Not cancelTransfer Then
        ReDim binaryData(bytesTotal)
        Winsock1(Index).GetData binaryData, vbArray + vbByte
        Put #ff, , binaryData
        cp = Loc(ff)
        p = CInt(cp / fileSize * CDbl(100))
        If p <> lastP Then
            If p >= 0 And p <= 100 Then
                Me.Caption = p & "% Transferred..."
                ProgressBar1.Value = p
                DoEvents
            End If
            lastP = p
        End If
    End If
End Sub

Private Sub processInputs()
    Dim markerPos As Integer
    Dim curInput As String
    Dim values As Variant
    Dim newSock As Byte
   
    ' see if we have a msg in our buffer
    markerPos = InStr(inputBuffer, Chr(1))
    Do While markerPos > 0 ' if we have a msg then process it
        curInput = Left$(inputBuffer, markerPos - 1) ' get msg from buffer
        inputBuffer = Mid(inputBuffer, markerPos + 1) ' remove msg from buffer
       
        values = Split(curInput, Chr(0)) ' split msg into command and its values
        Select Case values(0) ' what command was it?
            Case "r" ' receive file
                receiveFile values
           
            Case "s" ' send file
                If values(1) = fileName Then
                    cancelTransfer = False
                    resumePoint = CLng(values(2))
                    Winsock1(1).RemoteHost = Winsock1(0).RemoteHost
                    Winsock1(1).RemotePort = CLng(values(3))
                    Me.Caption = "Connecting to Port " & Winsock1(1).RemotePort & "..."
                   
                    On Error GoTo getNewPort:
getAnotherPort:
                    Winsock1(1).Close
                    Do While Winsock1(1).State <> sckClosed
                        DoEvents
                    Loop
                    Winsock1(1).LocalPort = 0
                    Winsock1(1).Connect
                End If
               
            Case "c" ' transfer cancelled by recipient
                Me.Caption = "File Transfer Cancelled by Recipient"
                MsgBox "File Transfer Cancelled by Recipient"
                Command3.Enabled = True
               
            Case "p" ' transfer pending
                Me.Caption = "File Transfer is Pending..."
                Command3.Enabled = False
               
            Case "x" ' transfer in progress cancelled
                Me.Caption = "Transfer Cancelled"
                cancelTransfer = True
                MsgBox "Transfer Cancelled"
               
            Case "m" ' chat message
                RichTextBox1.SelStart = Len(RichTextBox1.Text)
                RichTextBox1.SelLength = 0
                RichTextBox1.SelColor = vbRed
                RichTextBox1.SelText = values(1) & vbCrLf
               
        End Select
       
        markerPos = InStr(inputBuffer, Chr$(1)) ' any more msgs in buffer?
    Loop
    Exit Sub
   
getNewPort:
    Resume getAnotherPort
End Sub

Private Sub receiveFile(params As Variant)
    On Error GoTo cancelled
   
    Dim retVal As Integer
   
    CommonDialog1.CancelError = True
    CommonDialog1.InitDir = App.Path
    CommonDialog1.fileName = params(1)
    CommonDialog1.DialogTitle = "Accept Transfer of file? " & params(1) & " (" & params(2) & " kb)"
    CommonDialog1.ShowSave
   
    ' open file and get ready to receive it....
    If Dir(CommonDialog1.fileName) <> "" Then
        resumePoint = FileLen(CommonDialog1.fileName)
        If CommonDialog1.FileTitle = params(1) Then
            ' local file of same name already exists
            If resumePoint < CLng(params(2)) Then ' resume download?
                retVal = MsgBox("Resume Download?", vbYesNoCancel, "Partial File Found")
                Select Case retVal
                    Case vbNo
                        resumePoint = 0
                    Case vbCancel
                        GoTo cancelled
                End Select
            ElseIf resumePoint = CLng(params(2)) Then ' overwrite?
                retVal = MsgBox("Overwrite Existing File?", vbYesNoCancel, "File Already Downloaded")
                Select Case retVal
                    Case vbYes
                        resumePoint = 0
                    Case vbNo, vbCancel
                        GoTo cancelled
                End Select
            Else ' target file is larger than source...delete target?
                retVal = MsgBox("Replace Existing File?", vbYesNoCancel, "Target File Larger than Source File")
                Select Case retVal
                    Case vbYes
                        resumePoint = 0
                    Case vbNo, vbCancel
                        GoTo cancelled
                End Select
            End If
        End If
    Else
        resumePoint = 0
    End If
    ff = FreeFile
    Open CommonDialog1.fileName For Binary Access Write As #ff
    Seek #ff, resumePoint + 1
       
    fileSize = CLng(params(2))
   
    On Error GoTo newPort
selectNewPort:
    Winsock1(1).Close
    Do While Winsock1(1).State <> sckClosed
        DoEvents
    Loop
    Winsock1(1).LocalPort = 0
    Winsock1(1).Listen
   
    cancelTransfer = False
    Me.Caption = "Waiting for File Transfer on Port " & Winsock1(1).LocalPort & "..."
    Winsock1(0).SendData "s" & Chr(0) & params(1) & Chr(0) & resumePoint & Chr(0) & Winsock1(1).LocalPort & Chr(1)
    Exit Sub
   
newPort:
    Resume selectNewPort
   
cancelled:
    Winsock1(0).SendData "c" & Chr(1)
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
    If KeyAscii = 13 And Text1.Text <> "" Then
        If Winsock1(0).State = sckConnected Then
            RichTextBox1.SelStart = Len(RichTextBox1.Text)
            RichTextBox1.SelLength = 0
            RichTextBox1.SelColor = vbBlue
            RichTextBox1.SelText = Text1.Text & vbCrLf
            Winsock1(0).SendData "m" & Chr(0) & Text1.Text & Chr(1)
            Text1.Text = ""
        End If
        KeyAscii = 0
    End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Dim a As Integer
   
    ' shut all winsocks down
    For a = Winsock1.UBound To 0 Step -1
        If Winsock1(a).State <> sckClosed Then
            Winsock1(a).Close
        End If
    Next a
   
    Close ' close any open files
End Sub
0
 
LVL 7

Assisted Solution

by:wesbird
wesbird earned 100 total points
ID: 12068053
>> Also is 2800 bytes a good chunk size ? Should it be smaller then this or larger then this? If I change it to say 5600 bytes then will I face loss of Packets? or not?

You'll lose 40 bytes in protocol overhead for each chunk so smaller chunks means less efficiency.  On the flipside as we discussed earlier the MTU will be 576 bytes so your chunks are already fragmented beneath TCP.  If you make the chunk sizes larger as in Idle_Mind's example (4096) then it will reduce the amount of time that you're negotiating instead of sending - you won't lose any data unless the stream is broken.  

>> Server: I'm ready to send you a file now!
>> Client: Ok, go ahead and send me the first chunk.
>> Server: Here it is!
....

One interesting point is that you shouldn't need to wait for each chunk to be delivered individually before sending the next one.  It sounds as if you've designed a Synchronous protocol.  An alternative is to keep sending back to the source the chunk number of the last chunk received.  As long as this keeps increasing (even though it's not always the last one you sent) - just keep pumping the data in as fast as you can and make sure that the acks are in the correct sequence (they should always be) and then just wait for the last acknowlegement at the end when you run out of data to pump in, i.e. an Asynchronous protocol instead.

This may sound silly but I think it's a good analogy - think of the TCP stream like a pipe of water - the pipe contains a certain volume water which is in transit.  If you chuck the water through a bucket at a time you have to wait for it to reach the other end before sending the next bucketful - which will get slower and slower the longer the pipe - this may be what's happening with your protocol and would normally give you a huge performace boost.

.

 
   
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 12068178
If you study my implementation of dual winsocks closely, you will see that there is no need for an ack signal at all since you get a SendComplete() event when the last chunk was successfully received.  The first chunk is sent from the Connect() event and then the rest of file is sent in chunks each time SendComplete() fires.  After sending that first chunk, the code never leaves the SendComplete() sub unless you receive a chat message.

Idle_Mind
0
 
LVL 13

Author Comment

by:imarshad
ID: 12068553
VERSION 5.00
Object = "{248DD890-BB45-11CF-9ABC-0080C7E7B78D}#1.0#0"; "MSWINSCK.OCX"
Object = "{3B7C8863-D78F-101B-B9B5-04021C009402}#1.2#0"; "RICHTX32.OCX"
Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0"; "COMDLG32.OCX"
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"

I never knew that VB programs can be written like this......I think I will need quite lot of time to understand your code and then embed in my code......I will try it tomorrow...... By the way what is the advantage of using OCX controls like this and not adding them from Components Tab?

and similarly
   Begin VB.CommandButton Command3
      Caption         =   "Command3"
      Height          =   375
      Left            =   3000
      TabIndex        =   2
      Top             =   600
      Width           =   1335
   End
What is the advantage of using it like this rather then using the standard method of adding a Command Button on the Form and changing its properties ?

I see one big advantage for me......My boss will be more impressed with the total no. of lines increased significantly (Laughing out loudly)......(He is a non IT person and don't know much about programming).........

Imran Arshad
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 12068767
That is the way VB stores your form in the form file.  I did not create that code manually, VB updates all that information as you add and/or change controls to the form and change the properties of those controls.  You can view your forms code by simply opening it with Notepad.  =)

Follow the instructions I gave above the code and in my last post to recreate the application.  Experiment with the app as it is before trying to integrate into your application.   The app I just gave you has a very different architecture than what you were making before.

Here are my instructions from the last post:

    To create this application, paste the code below into notepad and save it as Form2.frm.  Then create a new project and add the newly created form to it.  Remove the default form from the project.  Be sure to go in to Project --> Properties and reset the Startup Object field.  Compile the application and run two instances of the EXE.

    Hit the Listen button on one of the instances.  In the other instance, type in the IP shown or the local IP (127.0.0.1) and hit connect.  Once you have a connection, you may chat between the applications.  Now hit the Transfer button.  A File Open dialog box will appear allowing you to select the file to send.  Once the file has been selected, a Save As dialog box will appear for the other application.  If the receiver accepts the file then transfer will begin.  Chatting my continue while the file is being transferred.

Regards,

Idle_Mind
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 12068866
Actually, by build instructions are slightly out of order.  After saving the code through notepad into Form1.frm (the name doesn't matter), create a new project and remove the default form.  Then you can add your newly created form to the project throur Project --> Add Form --> Existing.

Idle_Mind
0
 
LVL 13

Author Comment

by:imarshad
ID: 13497069
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

There is an easy way, in .NET, to centralize the treatment of all unexpected errors. First of all, instead of launching the application directly in a Form, you need first to write a Sub called Main, in a module. Then, set the Startup Object to th…
The purpose of this article is to demonstrate how we can use conditional statements using Python.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now