Solved

Winsock Help needed....... (Part 1)

Posted on 2004-09-01
16
880 Views
Last Modified: 2013-11-13
Hi all,
         I am developing a system in which I will have a central server....... There will be some clients (at the maximum 10)..
These clients will connect to the Server and will get updated..... The updation is  simply the updation of a  text file that is present both at the Server and Client (Server will alway have the most updated text file).....I decided to use a Winsock Control for the Data Transfer and the Server-Client will communicate with the following sequence........

Server                                                                                       Client
                         
    <------------------------------Establish a Connection--------------------

    <----------------------------(Update Me)

                                        (Ready to Send File)------------------------->

   <----------------------------( I have x Lines/Bytes)                                   (So that only changes are transmitted)

                                        (Ok) ------------------------------------------>

                                       ("###") -------------------------------------->      (Tells to the client start of sending Data)

                                        (Data)---------------------------------------->      (Actual Data to be send)

                                      ("***") --------------------------------------->      (Tells the client End of sending Data)


  <------------------------------( I have Received x Lines/Bytes)


                                           (Ok) ---------------------------------------->

                  <---------------------------End Connection-------------------->


I have done some coding for this but I am stuck in some points.........So I want you people to help me out in this problem...
and let me tell u I am almost a novice as the Network Programming (Winsock Control ) is concerned


Connection Establishment......

I think that at the client side the connection establishment is very simple.......
Here is some of my code......

Private Sub Form_Load()

        WskClient.Connect "LocalHost", 1001
        If Err <> 0 Then
            WskClient.Close
        End If
WskClient.SendData "Msg_Update"

End Sub



Private Sub WskClient_DataArrival(ByVal bytesTotal As Long)
    Dim recBuffer As String
   
    WskClient.GetData recBuffer
    MsgBox recBuffer
        Select Case Left(recBuffer, 10)
    Case "Msg_RdySnd"  'Block Received
        xLines = CalculateNoOfLines()
        WskClient.SendData "Msg_xLines" & xLines
   
    Case "Msg_OKLine"
        xLines1 = Right(recBuffer, 10)
           
    Case "##########"
         StartOfFIle=True  

    Case "**********"
          EndOfFile=True
    Case "Msg_OkS"  'Ok you can begin to send file
        ReceiveFile( )
    Case Else
       
    End Select
End Sub

But the problem is at the Server SIde.......
Here is my code at the server side.....

Private Sub Form_Load()
    WskServer(0).Protocol = sckTCPProtocol
    WskServer(0).LocalPort = 1001
    WskServer(0).Listen
    intMax = 0
   
End Sub


Private Sub WskServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
    If Index = 0 Then
        intMax = intMax + 1
       
        Load WskServer(intMax)
        WskServer(intMax).Accept requestID
    End If
    lblInfo.Caption = "Connection Estabilished..."
End Sub

Private Sub WskServer_DataArrival(Index As Integer, ByVal bytesTotal As Long)
    Dim RecBuffer As String
    Dim j As Integer
    On Error GoTo GLocal
   
    WskServer(Index).GetData RecBuffer
    Select Case Left(RecBuffer, 10)
        Case "Msg_UPdate"
       
                WskServer(Index).SendData "Msg_RdySend"
               
        Case "Msg_xLines"
                NoOfLinesClient = Right(RecBuffer, 10)
                WskServer(Index).SendData "Msg_OKLine" & NoOfLines
                'xLines = LinesToSend(NoOfLines)
                WskServer(Index).SendData "##########"
                Call SendFile(NoOfLinesClient)
                WskServer(Index).SendData "**********"
        Case "Msg_RcvLin"
                RecLines = Right(RecBuffer, 10)
                'If not received all lines then send again
                WskServer(Index).SendData "Msg_OK_Snd"
        Case "Msg_Thanks"
                WskServer(Index).Close
        Case Else
       
        End Select
    Exit Sub
GLocal:
    MsgBox Err.Description
    Unload Me
   
End Sub


This code is almost the same as the one in MSDN......

Problem 1::

Since I donot know much about Winsock so here are a few questions that are stuck into my mind......

1) As the code illustrates that WskServer(0) is always in listening mode and whenever a new Request arrives it simply creates a new instance of of WskServer which accepts the connection.......So if my Server is running 24hrs/ 7 days then intMax=intMax+1 will cause the intMax value to be out of bounds of Long within a couple of Weeks.....How to avoid that?

2) Since I will have fixed no. of Clients that can communicate with my Server say only 3 then do I need to use it in this way that I have shown in the example?
How can I use it like this "if Client1 connects with me then Load WskServer(1) similarly for Client2 load WskServer (2) and when it disConnects UnLoad its respective Control i.e like Unload WskServer(1)  after Client 1 is being updated completely.....

3) How do the Server knows that Client1 is still connected with me  ?

4) How do the Client1 knows that it is still connected with the Server ?


Note:::::

This is the first Part of this series of Questions almost all of which will be focussing around Winsock.......

Imran Arshad
0
Comment
Question by:imarshad
  • 8
  • 3
  • 3
  • +1
16 Comments
 
LVL 13

Author Comment

by:imarshad
ID: 11951631
Please do tell me some suggestions to improve my code or even better way of connecting and handshaking (Sort of)  in comparison to what I am doing right now.......
0
 
LVL 10

Expert Comment

by:anv
ID: 11951782
visit the following link
of VBIP

they provide lot of help on using winsock control

http://www.vbip.com

also
following link
http://www.winsockvb.com

u'll get ur solutions on these two links...

u'll have to do some reading get the solution
0
 
LVL 10

Assisted Solution

by:anv
anv earned 75 total points
ID: 11951795
0
 
LVL 22

Assisted Solution

by:DarkoLord
DarkoLord earned 75 total points
ID: 11951832
1) for the first question you can look at this example: http://www.winsockvb.com/article.php?article_id=19
2) it is best to use the method that you are currently using
3) and 4) Winsock is maintaining the connection so it assumes they are connected until one of them disconnects --> Winsock_ConnectionClosed event

Darko
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 350 total points
ID: 11954415
Below is a basic server that can handle multiple clients and recycle unused winsock controls.

The biggest problem I see however is in your handling of the incoming stream in the DataArrival() Event.  In a perfect world you will receive the packets exactly as they were sent.  In the real world though, packets often get chopped up along the way.  For instance, if you send "This is a message" from the client, it may actually arrive at the server as two different DataArrival() events; one with "This is a me" and the other with "ssage".  There is no way to prevent this and it is not seen as a bug.  The TCP protocol simply states that the information you send will arrive in the order you sent it, but not neccessarily in the chunk size you sent it.  Along the same lines, if you send two different messages, "Cats" and "Dogs", they may arrive as one message appended together as "CatsDogs".   It is also unsafe to use the same buffer for all clients since you may get part of a message from one client and then immediately receive part of a message from another client.  

To get around these problems you need to mark the end of your message with some kind of control character, or sequence of characters, and keep seperate stream buffers for each client.  Furthermore, you must store your incoming stream in a persistent buffer so that you can append the current stream data to what has arrived before.  In this manner, you can correctly handle a message that has been split into mulitple pieces.  In your implementation you declared RecBuffer as a local variable in the DataArrival() event.  If a message arrives in pieces, the previous portions will be lost.

In the example below I have used Chr(1) as an End of Message marker and Chr(0) as a parameter delimiter.  For instance, you could send:

    Dim msg As String
    Dim numLines As Integer
    Dim numBytes as Integer

    numLines = 5
    numBytes = 32
    msg = "UpdateInfo" & Chr(0) & numLines & Chr(0) & numBytes & Chr(1)
    Winsock1(index).SendData msg

Then on the receiving side you can know that you have the whole message by checking for the presence of Chr(1) and you can break the message into its parts by splitting that message with Chr(0).

Hope this has been helpful,

Idle_Mind



Option Explicit

Private inputBuffer() As String
Private users As Integer

Private Sub Form_Load()
    Me.Caption = "Server"
    users = 0
    ReDim inputBuffer(0)
   
    ' channel 0 is the only listening port
    Winsock1(0).LocalPort = 10001
    Winsock1(0).Listen
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Dim a As Integer
   
    ' shut everything down
    For a = Winsock1.UBound To 0 Step -1
        If Winsock1(a).State <> sckClosed Then
            Winsock1(a).Close
        End If
    Next a
End Sub

Private Sub Winsock1_ConnectionRequest(index As Integer, ByVal requestID As Long)
    Dim acceptIndex As Integer
    Dim curindex As Integer
   
    ' See if there is an available Channel already loaded
    acceptIndex = -1
    If index = 0 Then
        For curindex = 1 To Winsock1.UBound
            If Winsock1(curindex).State = sckClosed Then
                acceptIndex = curindex
                Exit For
            End If
        Next curindex
    End If
   
    ' Load a channel if none available
    If acceptIndex = -1 Then
        acceptIndex = Winsock1.Count
        Load Winsock1(acceptIndex)
        ReDim Preserve inputBuffer(acceptIndex)
    Else
        inputBuffer(acceptIndex) = ""
    End If
   
    ' Accept connection on Channel decided upon
    Winsock1(acceptIndex).LocalPort = 0
    Winsock1(acceptIndex).Accept requestID
   
    ' Increase number of Users Connected and notify new Channel
    users = users + 1
    Me.Caption = "Server : " & users & " User(s)"
End Sub

Private Sub Winsock1_Close(index As Integer)
    Dim curindex As Integer
   
    ' Channel has disconnected
    Winsock1(index).Close
   
    ' Decrease Total number of users and notify all other channels of disconnection
    users = users - 1
    Me.Caption = "Server : " & users & " User(s)"
   
    ' attempt to release unneeded Winsock Controls from end of array
    Do While Winsock1(Winsock1.UBound).State = sckClosed
        Unload Winsock1(Winsock1.UBound)
    Loop
    ReDim Preserve inputBuffer(Winsock1.UBound)
End Sub

Private Sub Winsock1_DataArrival(index As Integer, ByVal bytesTotal As Long)
    Dim inputString As String
   
    ' add current command to buffer and process the buffer
    Winsock1(index).GetData inputString, vbString
    inputBuffer(index) = inputBuffer(index) & inputString
    processInputs index
End Sub

Private Sub processInputs(index As Integer)
    Dim markerPos As Integer
    Dim curInput As String
    Dim values As Variant
       
    ' see if we have a msg in our buffer
    markerPos = InStr(inputBuffer(index), Chr(1))
    Do While markerPos > 0 ' if we have a msg then process it
        curInput = Left$(inputBuffer(index), markerPos - 1) ' get msg from buffer
        inputBuffer(index) = Mid(inputBuffer(index), markerPos + 1) ' remove msg from buffer
       
        values = Split(curInput, Chr(0)) ' split msg into its parts
        Select Case values(0)
            Case "a"
               
            Case "b"
           
            Case "c"
               
        End Select
       
        markerPos = InStr(inputBuffer(index), Chr$(1)) ' any more msgs in buffer?
    Loop
End Sub
0
 
LVL 13

Author Comment

by:imarshad
ID: 11961300
Thanks anv and darkoLord for your help......This site contains really good articles even for a novice like me....

Idle_Mind,
                Thanks for your help and source code......I have almost got the idea.....Here is what I have got from your Code Please correct me where I am wrong.....

1) WinSock(0) at the server side is always listening and its purpose is only to Listen the Connection Requests from Clietns....

2) WinSock(1) will always be connected to the first Client that is trying to connect....and so on for the remaining Clients....

3) inputBuffer(index) will contain all the Commands/Data that is received from the Client

4) All commands will contain chr(1) as an indication that the command is finished.....and if I am sending multiple commands in one command then it will be separated by chr(0)

So now I have a few questions.....

a) If I want to know which Client is trying to connect to the Server how can I know ?
i.e. if I want to know who is connected onto the server on the socket WinSock(1) ?
Do I need to send some parameters from Client like this
msg = "UpdateInfo" & "Client A" & Chr(0) & numLines & Chr(0) & numBytes & Chr(1)
Winsock_ClientA.SendData msg

so that the Server will know this is "Client A"  or is there some direct method of finding who is connected on WinSock(1) ?

b) What I want to achieve is a sequential flow of my program......i.e After connection my Client will send some Command (Message), the Server will (when received that command) follow up with a command to the client and so on.....Like I have shown in my question....

Now as you have said that my Command might be divided into many parts and I might recieve "This is a Message" as "This is a Mess" and "age".....As long as the sequence remains tha same I have no problem as the  inputBuffer(index) will contain the complete command "This is a Message" which I can recognise ,decode and acknowledge.....But what happens if I get it in wrong order Like "ageThis is a Mess" How to handle such situations ? Are such situations Possible at all ?

So according to my algo the Server should not acknowledge Client that it has received some Command/Request/Message from Client but in fact it has received......How to tackle this situation as the client will be waiting for a response to the Command it has send?

Once these Queries are answered I will post  "Part 2"  of this question that will be rotating around Sending Large Text on WinSock.......

imran Arshad
0
 
LVL 13

Author Comment

by:imarshad
ID: 11961329
>>I will post  "Part 2"  of this question

Of course as a new Question.....
0
 
LVL 22

Expert Comment

by:DarkoLord
ID: 11962443
a) If clients have static IPs you can use .RemoteHostIP property to get it's IP... otherwise you would have to send some "authentication" data first
b) Received data cannot be messed up, however you could receive multiple commands in one... thats why you should end each command with a nullchar (vbNullChar constand) as you can see in Idle_Mind's example above...

As for sending response that the command has been received, you don't need to do this... My advice is to split your communication to multiple stages and then you can use winsock's .Tag property to know which stage is each client in...

Also it is easy to mark each data with a number... for example: "hello" can be 1, "ok" can be 2 and so on.... you can then use Chr$(Command_ID) so it takes only one byte and you can parse it easily...

Darko
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 13

Author Comment

by:imarshad
ID: 11962961
>>As for sending response that the command has been received, you don't need to do this... My advice is to split your >>communication to multiple stages and then you can use winsock's .Tag property to know which stage is each client in...

Sorry I could not understand this concept.....What I am trying to do is to send some Command from Client and Trigger the next Event (Maybe send next command or send data or close connection)  in the Client only after the response from the first command has reached from the Server....Can you supply some source code for what you have suggested ?

>>If clients have static IPs you can use .RemoteHostIP property to get it's IP... otherwise you would have to send >>some "authentication" data first

So the method that I have suggested is good enough to work if I donot have Static IP?
msg = "UpdateInfo" & "Client A" & Chr(0) & numLines & Chr(0) & numBytes & Chr(1)
Winsock_ClientA.SendData msg
I wonder how "Huge Servers" like Kazaa and MSN Messenger work ? Since all of its clients donot have static IP.....Do my Messenger send my Information in the form of above example or do they also send my Dynamic IP  with my User Name ?
As far as I know one can check the IP address of whom one is chatting in MSN Messenger......


Imran Arshad

0
 
LVL 22

Expert Comment

by:DarkoLord
ID: 11963054
Well the source is very easy so it should only take a minute for you to write it... here's the concept:

Client:    Send command ----> server
Client:    Set .Tag property to "stage1"
Server:   Send response ----> client
Client:    checks which stage is now and then:
              Send command2 ----> server
              Set .Tag property to "stage2"
and so on...
so basically client always knows which response is it waiting for :)

However, are you ABSOLUTELY sure you really need to wait for response??


Easiest way of managin "Huge Servers" is to create a class for each user that contains his userinfo... some of the servers work with user hashes, others with IDs, etc...

Darko
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 11963073
1) WinSock(0) at the server side is always listening and its purpose is only to Listen the Connection Requests from Clietns....

Correct.  (Some people use a completely different Winsock control to always listen)

2) WinSock(1) will always be connected to the first Client that is trying to connect....and so on for the remaining Clients....

Initially the clients will be connected to the same Index number as the order that they connected in.  After people disconnect, the winsock control they were using is closed, BUT NOT unloaded.  Subsequent connections can then serviced using those already loaded winsock controls.  For instance, if you have three clients connect then they would serviced like this:

    Index 1 --> Client #1
    Index 2 --> Client #2
    Index 3 --> Client #3

Then if Client #2 disconnects it will look like this:

    Index 1 --> Client #1
    Index 2 --> sckClosed
    Index 3 --> Client #3

Now when the next client connects they will be serviced using Index 2 since it is available and no new winsock is loaded at this time:

    Index 1 --> Client #1
    Index 2 --> Client #4
    Index 3 --> Client #3

Now if Client #1 and Client #4 disconnect you will have this:

    Index 1 --> sckClosed
    Index 2 --> sckClosed
    Index 3 --> Client #3

Index 1 and Index 2 will not be unloaded because there is still a client connected above them.  Unloading them would leave a literal hole in the index array that makes the code nasty since you have to check if the index is instatiated before using it.  This requires Error trapping and makes the code harder to read.  Once Client #3 disconnects, Index 1, Index 2 and Index 3 will all be unloaded leaving only Index 0 listening for connections.

3) inputBuffer(index) will contain all the Commands/Data that is received from the Client

Correct.  DarkoLord suggests using the .Tag property to store each clients stream buffer.  Either way will work fine since they are both persistent and not local to the DataArrival() event.

4) All commands will contain chr(1) as an indication that the command is finished.....and if I am sending multiple commands in one command then it will be separated by chr(0)

Yes and no.  The way I had coded it, a message is ended with Chr(1).  The purpose of Chr(0) was to delimit values for the SAME command so you could easily send related pieces of data like header information for the size of you file.  With my code you wouldn't send more than one command seperated by Chr(0), only Chr(1).   This is just the way I chose to do it.  You need to decide on a protocol that works for your situation and pick delimiters that make sense.  In a plain text chat application, Chr(1) and Chr(0) work great.   If you are sending binary data, the don't work so great since those characters may actually exist in the stream.  In that case you could use longer delimiters that are unlikely to occur in the stream.

a) If I want to know which Client is trying to connect to the Server how can I know ?

As Darko said, unless your clients have Static IPs then they will have to "login" once they connect.  This requires a small bit of setup on the client side unless you can install each one with some kind of unique identifier in a data file to use as a login ID.  You would just make your own protocol for the login message like (if you have passwords):

    msg = "Login" & Chr(0) & "Client A" & Chr(0) & Password & Chr(1)
    Winsock_ClientA.SendData msg

b) What I want to achieve is a sequential flow of my program

You wont get the packets out of order using the TCP protocol.  That is possible using the UDP protocol however, which is why you don't see it used very often for file transfers since you have to manage the packets very carefully at a low level and possibly store them until you receive missing packets to fill in the holes.

Regards,

Idle_Mind
0
 
LVL 13

Author Comment

by:imarshad
ID: 11963217
>>However, are you ABSOLUTELY sure you really need to wait for response??

Yes because some of these might contain data that is going to be used in the subsequent commands.....Like the NoOfBytes or NoOfLines parameter that will tell me what chunk of File I need to send(the unupdated part of file).....

I will come back in about 90 min. and then I will try to Close this question and post the New Question....

Imran Arshad
0
 
LVL 13

Author Comment

by:imarshad
ID: 11964711
>>You wont get the packets out of order using the TCP protocol.
>>The TCP protocol simply states that the information you send will arrive in the order you sent it, but not neccessarily in
>> the chunk size you sent it.

I had missed it in your earlier post......SO this is good news for me.....

Also what will happen if  both server and Client are connected but one of them gets disconnected from the internet while protocol \ command interchange is still in progress??
Will the ServerWinsock_Close( ) and ClientWinsock_Close( ) event be fired at both ends ? Since I am currently developing both the Server and Client on the same system I don't know how will it react on the internet.....

I think that whenever a connection is closed then I need to flush inputBuffer(index) for that index that has been closed...
I donot know what this command is doing

ReDim Preserve inputBuffer(Winsock1.UBound)

But lets say I have this situation

    Index 1 --> Client #1
    Index 2 --> Client #2
    Index 3 --> Client #3

and then Client # 3 gets disconnected

and  a new Client # 4 comes then the following will be the situation

    Index 1 --> Client #1
    Index 2 --> Client #2
    Index 3 --> Client #4

and the inputBuffer(3) will contain the previous data as well as the newer data........Isn't it like this?

Also what will be the safest way to close the connection...... i.e. the client has received the file....Do I need to close the connection from the Client Side or the Server side ? and Is this line enough at the client side ?
WinsockClient.close

Imran Arshad

0
 
LVL 13

Author Comment

by:imarshad
ID: 11965276
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 11965626
>> what will happen if  both server and Client are connected but one of them gets disconnected from the internet while protocol \ command interchange is still in progress??
Will the ServerWinsock_Close( ) and ClientWinsock_Close( ) event be fired at both ends ?

I believe you will get an Error event from the winsock control notifying you, but I'm not actually sure as it is hard to create those conditions when both the server and client are being tested on the same machine.  One thing you can do is always check the state of the connection before attempting to send anything:

    If Winsock1(0).State = sckConnected Then
        Winsock1(0).SendData "some data..."
    End If

>> I need to flush inputBuffer(index) for that index that has been closed...

I am clearing the input buffer when the connection is accepted in ConnectionRequest():

    ' Load a channel if none available
    If acceptIndex = -1 Then
        acceptIndex = Winsock1.Count
        Load Winsock1(acceptIndex)
        ReDim Preserve inputBuffer(acceptIndex)
    Else
        inputBuffer(acceptIndex) = ""
    End If

If acceptIndex = -1 then we have to load up a new winsock control to handle the new connection.  If this is the case then we also have to increase the size of the input buffer array to match the size of the winsock control array.  This is what the

    ReDim Preserve inputBuffer(acceptIndex)

line does.  It makes the inputBuffer array the same size as the winsock control array.  The Preserve word keeps the existing data in the array so it doesn't get lost.

>> Do I need to close the connection from the Client Side or the Server side ?

It doesn't matter but personally I would make the client side initiate the disconnect so that the close event will fire at the server side and make it clean up the winsock control array.

>> and Is this line enough at the client side ?
WinsockClient.close

Yes.

Regards,

Idle_Mind
0
 
LVL 13

Author Comment

by:imarshad
ID: 11965936
Thanks Idle_Mind for your help and I will love to see you participate in the other Question as well as I have gained quite a few things while discussing with you......

Imran Arshad
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
bigHeights  challenge 13 56
countHi2 challenge 7 44
mapShare challenge 13 69
Excel - Save a copy of work book 13 80
How to remove superseded packages in windows w60 or w61 installation media (.wim) or online system to prevent unnecessary space. w60 means Windows Vista or Windows Server 2008. w61 means Windows 7 or Windows Server 2008 R2. There are various …
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

760 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

21 Experts available now in Live!

Get 1:1 Help Now