• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1480
  • Last Modified:

TCP/IP with Winsock Control and VB 6

Hi.

I am using VB 6 with Winsock Control to build a TCP/IP based client. The server is NOT written by me and I don't have access to its source code, therefore I can't change the server in any way.

The client connects to the server through TCP/IP and then sends some commands for the server. The problem is that the server is built with the assumption that each TCP/IP packet would contain only ONE command therefore no delimiter is used for separating commands. Now when my client application sends multiple commands by calling SendData() method of Winsock control for each command, the Winsock combines those small packets with commands into a larger packet and then sends it. Winsock uses "Nagle algorithm for send coalescing", as mentioned in MSDN.

When this packet with multiple commands is received by the server, it is not accepted by it because now it has more than one command in a packet therefore it's not according to its format.

Therefore, what I need, is a way to “flush” the data before calling the SendData() method again so that new command is not concatenated with the previous ones.

From MSDN I came to know about a Winsock option "TCP_NODELAY", which does this, but it does not seem to work for me. Although I set this option but the behavior of the Winsock remained the same (I used sniffer to examine the packets). I also came to know that SDK for Win CE contained a bug which caused Winsock to ignore this option value. But I didn't find any article which indicated that this bug also exists in Win 9x/2000/XP.

I think an example would clear what I want to do.

Example
----------

I want to send some text/commands on TCP connection. They should be send in a way such that each packet contain only one command:

    Packet 1 -->    Command_1
    Packet 2 -->    Command_2
    Packet 3 -->    Command_3

But when I send the data using SendData() method of Winsock control:

    Wsk.SendData("Command_1")
    Wsk.SendData("Command_2")
    Wsk.SendData("Command_3")

Winsock combines all three commands in a single packet as :

    Packet 1 -->    Command_1Command_2Command_3

While I want winsock to send each command in a different TCP packet. That is, I want to packetize the TCP stream. I tried to enter some delay between SendData() calls but many times it also results in the concatenation of the commands. Besides this, I read in one article that the routers or destination end-point may combine the packets.

I would highly appreciate if you can help me.

Regards.
0
jazzzz20
Asked:
jazzzz20
  • 9
  • 4
  • 2
  • +2
1 Solution
 
leavinmateCommented:
How about adding one of the ASCII control characters to the end of the string?  Either a

EOT::=character_nbr(4)::= End Of Transmission(ASCII'EOT).

Or,

CR::=character_nbr(13)::= Carriage Return to left margin(ASCII'CR)\r.

I would add call with this line:
   Wsk.Sendata ("Command_1" & Chr$(4))

to Try it.

Another thing to try is to add a few DoEvents lines after each call.  However, this may cause issues if you're logic depends on all three calls to finish before moving on.

   Wsk.SendData("Command_1")
   DoEvents
   Wsk.SendData("Command_2")
   DoEvents
   Wsk.SendData("Command_3")
   DoEvents


FYI:
A full list of Control Characters can be found here:

http://www.csci.csusb.edu/dick/samples/comp.text.ASCII.html#Control%20characters
0
 
leavinmateCommented:
Oh and another good control character to try might be the NUL  (Chr$(0))  

This is the string delimiter in windows and might work as well.
0
 
MrDarkCommented:
why don't you try to close the connection and connect.
try it it may work. The code will be:

wsk.Connect
wsk.SendData ("Command_1")
wsk.Close
wsk.Connect
wsk.SendData ("Command_2")
wsk.Close
wsk.Connect
wsk.SendData ("Command_3")
wsk.Close
wsk.Connect
wsk.SendData ("Command_4")
wsk.Close
wsk.Connect
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
jazzzz20Author Commented:
Hi leavinmate.

I tried all the control characters (including NULL) as you said but it's still combining packets :(

I have tried DoEvents. But it is also of no use. I even tried it in a loop of 10000, but again, it didn't work.

I'm currently using a Timer control to cause a delay of 15 seconds between commands. 15 seconds between any two SendData() call seems a suitable delay but even it combines data when network is a little bit high on traffic. I need a way which should never combine packets.

Anyhow thanks for helping me.

Regards.
0
 
amaronjobCommented:

If your server returns any reply on successful execution of a command, you can use "DataArrival Event" and issue next command.

I am sure this will not help if your server not going to reply.
0
 
amaronjobCommented:

If your server returns any reply on successful execution of a command, you can use "DataArrival Event" and issue next command.

I am sure this will not help if your server not going to reply.
0
 
stretch71Commented:
First point to note is that there is no guarantee that your receiving server will not coalesce your packets on the receiving end, and make all your hard work on the sending end useless.

Also, padding any packet with control characters is never really a good idea since you have no guarantee over how those packets may arrive and where packet splits may occur.  Routers and other networking gear may make a mess of any nicely formed data you send and could easily coalesce your packets mid transmission.

Anyway,  you need to do two few things from the client side to get it working

1.  Set the Winsock buffer to 0 (SO_SNDBUF) so that winsock will not buffer your packets and puts them on the wire immediately

2.  Set TCP_NODELAY so you don't have to wait for the ACK from the receiving end before sending another packet.

For more info see MS Knowledge base article 214397.

From what I read it looks like you only set TCP_NODELAY.  Hope that this helps.
0
 
jazzzz20Author Commented:
Hi amaronjob.

The server does not send a reply on successful execution of the command. Therefore DataArrival event can not be used :-(

Regards.
0
 
jazzzz20Author Commented:
Hi MrDark.

If we close the Winsock while it has some pending data (which needs to be sent) in the buffer, then it is lost.

We can cause a delay between SendData() and Close() but still it gives no surity that data has been sent and it will not lost due to closing of winsock.

wsk.Connect
wsk.SendData ("Command_1")
'****** DELAY ******
wsk.Close
wsk.Connect
wsk.SendData ("Command_2")
wsk.Close

Regards.
0
 
jazzzz20Author Commented:
Hi stretch71.

I didn't set the SO_SNDBUF to 0. Now I would try it.

I am currently reading the KB article that you told. I'll let you know the result after I set buffer value.

Regards.
0
 
jazzzz20Author Commented:
Hi stretch71.

I didn't set the SO_SNDBUF to 0. Now I would try it.

I am currently reading the KB article that you told. I'll let you know the result after I set buffer value.

Regards.
0
 
MrDarkCommented:
why don't you try the sendcomplete property before sending another command so your code will be:

wsk.SendData ("Command_1")
do until wsk.sendcomplete = true
loop
wsk.SendData ("Command_2")
do until wsk.sendcomplete = true
loop
wsk.SendData ("Command_3")
do until wsk.sendcomplete = true
loop
wsk.SendData ("Command_4")
 may be you will have to use a separate procedure 4 that :)
0
 
MrDarkCommented:
sorry man i was talking wrong :(
use the sendcomplete event. to do that you will have to make a separate variable that holds the next command so you code will be:

dim my_command as string

Private Sub wsk_SendComplete()
   wsk.SendData (my_command)
End Sub
0
 
jazzzz20Author Commented:
Hi stretch71 & MrDark.

It works fine when SO_SNDBUF is set to 0 and there is some delay between SendData() calls.

It works much better when SO_SNDBUF is set to 0 and SendComplete event is used for the next SendData() call (as MrDark told).

It means that SO_SNDBUF is must for both solutions. But SO_SNDBUF causes a problem. After setting SO_SNDBUF = 1,
when the application starts sending the commands by calling SendData(), the application stop responding to user events (like click on buttons, window position changed, resized, etc.). It starts responding the events again when all the commands are sent.

I even tried putting "DoEvents" between calls, to process pending events, but it's not working.

I would appreciate if it can be fixed because it's not good to see your application does not respond to user generated events.

Thanks & Regards.
0
 
stretch71Commented:
Setting SO_SNDBUF = 1 means that your winsock control will have a 1 byte buffer.  It also means you are most likely going to be sending 1 data byte per packet.  If you also have TCP_NODELAY off then you will have to wait for a response to each of your packets and this could be slow.

The problem you really have relates to the lack of threading in VB6.

I can't remember off the top of my head but you will probably find that the SendData call is blocking.  This means your VB code won't actually continue until after the SendData call has completed, which means that you won't execute the doevents statement until all your data is sent.

Also, since VB6 doesn't thread you can't really prevent this behaviour in the program.  The only way to get around this is to either split you application (difficult/problematic), convert to c++ (messy), try to move to VB.NET and work with threading (do-able with a learning curve) or change your cursor to the hourglass before the senddata and back to normal once the calls have completed.  At least this way the user knows that something is happening and that they shouldn't panic, and it's very easy.

I know it's not the answer you are looking for, but I hope it helps.
0
 
jazzzz20Author Commented:
Hi stretch71.

I wish threading could be done in VB6 easily :-(

Your suggestions are indeed good, especially the suggestion of hour glass :-)

I think only MrDark is interested in answering the question besides you. Therefore I would wait for another day. If no one else answers the question, then the points are yours. But I would also give points to MrDark (without splitting this 250) because his solution can improve the result.

Regards.
0
 
MrDarkCommented:
Hi jazz,
why don't you try to put your send code in a timer?
0
 
jazzzz20Author Commented:
Hi MrDark.

I was checking it in the SendComplete event for the testing purpose. While I am already using timer control in the actual application as I told leavinmate.

I appreciate your help. As stretch71's answer is more correct therefore I am accepting his answer. But I am also initiating a new question for giving you points because you also gave helpful suggestions. Please collect your points:

http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_20567542.html

Thanks & Regards.
0
 
jazzzz20Author Commented:
Hi.

I thank every one who suggested solutions for this problem. I appreciate your help.

Regards.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 9
  • 4
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now