Link to home
Start Free TrialLog in
Avatar of GivenRandy
GivenRandy

asked on

UDP Server with Multiple Clients -- Simultaneously

I have a UDP Server (not TCP) that can work with multiple clients using Winsock control (the clients are on the same machine but different port numbers or may be on different machines). However, the Server does not work with the clients simultaneously. It seems to only communicate properly with the first client. I want the control to respond to each client's request, but it does not seem to be doing that.

Make three projects and executables: Server, Client1 and Client2.

For Server. Add textbox "txtSend", textbox "txtReceived", commandbutton "cmdSend" and winsock "sckServer" to the form. Set "sckServer" Protocol to sckUDPProtocol. Use this code in the form:

---
Option Explicit

Private Sub cmdSend_Click()
   sckServer.SendData txtSend.Text
End Sub

Private Sub Form_Load()
   With sckServer
       .RemoteHost = "localhost"
       .RemotePort = 1002
       .Bind 1001
   End With
End Sub

Private Sub sckServer_DataArrival(ByVal bytesTotal As Long)
   Dim strData As String
   
   sckServer.GetData strData
   txtReceived.Text = txtReceived.Text & strData & vbCrLf
End Sub
---

For Client1. Add textbox "txtSend", textbox "txtReceived", commandbutton "cmdSend" and winsock "sckClient" to the form. Set "sckClient" Protocol to sckUDPProtocol. Use this code in the form:

---
Option Explicit

Private Sub cmdSend_Click()
   sckClient.SendData txtSend.Text
End Sub

Private Sub Form_Load()
   With sckClient
       .RemoteHost = "localhost"
       .RemotePort = 1001
       .Bind 1002
   End With
End Sub

Private Sub sckClient_DataArrival(ByVal bytesTotal As Long)
   Dim strData As String
   
   sckClient.GetData strData
   txtReceived.Text = txtReceived.Text & strData & vbCrLf
End Sub
---

For Client2. Add textbox "txtSend", textbox "txtReceived", commandbutton "cmdSend" and winsock "sckClient" to the form. Set "sckClient" Protocol to sckUDPProtocol. Use this code in the form (note the change for the port number, from 1002 to 1003):

---
Option Explicit

Private Sub cmdSend_Click()
   sckClient.SendData txtSend.Text
End Sub

Private Sub Form_Load()
   With sckClient
       .RemoteHost = "localhost"
       .RemotePort = 1001
       .Bind 1003
   End With
End Sub

Private Sub sckClient_DataArrival(ByVal bytesTotal As Long)
   Dim strData As String
   
   sckClient.GetData strData
   txtReceived.Text = txtReceived.Text & strData & vbCrLf
End Sub
---

Compile all of those to executables (Server, Client1, Client2).

+ start Server
+ start Client1
+ start Client2
+ from Client1, type "Client1" and select "Send"
+ on Server, verify that you see the message
+ from Server, type "Server" and select "Send"
+ on Client1, verify that you see the message
+ on Client2, verify that you do NOT see the message
+ from Client2, type "Client2" and select "Send"
+ on Server, verify that you see the message
+ from Server, type "Server" and select "Send"
+ on Client2, verify that you do NOT see the message
+ on Client1, verify that you see the message

The last two are not what I want (I want the reverse).
ASKER CERTIFIED SOLUTION
Avatar of AzraSound
AzraSound
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Dear Randy:

I have a UDP client/server solution and it is working, (5 clients all over Australia!).
I know what you are talking about, and have had similar problems.

Most of my problems got solved by not relying at all on the .RemoteHost property, rather the .RemoteHostIP property.

I also use a control array of winsocks controls. Each control has its own RemoteHostIP, RemotePort and Bind.
At the client end the winsock connects to a different port on the server, based on the value of an .ini file.

Hope this helps!
Avatar of inthedark
Avatar of GivenRandy
GivenRandy

ASKER

AzraSound, that may be the only approach. It would appear that the Winsock control is flawed.

When I get the DataArrival event in the Server, it DOES recognize the proper remote port and IP address (the latter does not change, since all of them are on the same machine). However, when it sends out a response, it does not send it to the correct port, even when the RemotePort shows that it is correct. For example, upon DataArrival it switches from 1002 to 1003 and stays at 1003 just before SendData, but the SendData actually ends up going to 1002.

By the way, I looked at my reference W. Richard Stevens books ("UNIX Network Programming" and "Advanced Programming in the UNIX Environment") and Bob Quinn's book ("Windows Sockets Network Programming") and they basically confirmed what we thought. Most of their (and my) focus has been on TCP instead of UDP). I will also post as a possible problem on Microsoft.
It sure does seem like a possible problem.  Debugging definitely shows that the server "knows" which port he should be sending data to, but, in the end, he only sends to the client who first initiated contact with him.  If I remember correctly, I tried it where the server defaulted to a RemotePort of 1002, but if a message from 1003 came first, the server successfully switched from 1002 to 1003 and sent the message back accordingly.  Once that initial connection is made, it seems the server never wants to let go.  A pretty stubborn connection-less protocol implementation wouldn't you say?   ;-)
AzraSound, that may be the only approach. It would appear that the Winsock control is flawed.

When I get the DataArrival event in the Server, it DOES recognize the proper remote port and IP address (the latter does not change, since all of them are on the same machine). However, when it sends out a response, it does not send it to the correct port, even when the RemotePort shows that it is correct. For example, upon DataArrival it switches from 1002 to 1003 and stays at 1003 just before SendData, but the SendData actually ends up going to 1002.

By the way, I looked at my reference W. Richard Stevens books ("UNIX Network Programming" and "Advanced Programming in the UNIX Environment") and Bob Quinn's book ("Windows Sockets Network Programming") and they basically confirmed what we thought. Most of their (and my) focus has been on TCP instead of UDP). I will also post as a possible problem on Microsoft.
... oops ... hit wrong button and sent double response.
That works for now and does not cause large time problems (it does impact throughput, but works). Thanks!
Like this:

---
Private Sub cmdSend_Click()
    Dim lngTempPort As Long
   
    With sckServer
        lngTempPort = .RemotePort
        .Close
        .RemoteHost = "localhost"
        .RemotePort = lngTempPort
        .Bind 1001
        .SendData txtSend.Text
    End With
End Sub
---
By the way, this KnowledgeBase article was updated a couple months ago, but it does not work:

http://support.microsoft.com/default.aspx?scid=kb;en-us;192564
p.s., of course you have to set both RemotePort and RemoteHostIP and should pass that information along with the packets. Still, it should not be necessary to actually close the socket each time.
Glad I could help  :-)

If you hear anything from MS about this issue, do update us.  It would be interesting to hear what they have to say.
I came across this...

From Microsoft Knowledge Base Article - 192564
----------------------------------------------------
This article was previously published under Q192564

SUMMARY
When a UDP message is received by a Winsock control, the RemoteHostIP property is set to the IP address of the remote machine, and the RemotePort property is set to the IP port of the remote UDP application. The previous properties values are overwritten.

This could create a problem if the user was not expecting data on that port from a different RemoteHostIP. Attempts to call the SendData method without setting these two properties to the appropriate values might send the data to an unplanned recipient.

Whenever a UDP socket is being used, you should always reset the RemoteHostIP and RemotePort properties to your own known values before calling the SendData method.
MORE INFORMATION

This is by design. At the Winsock layer, the sendto API requires a sockaddr structure of the remote UDP peer to send the message to, and the recvfrom API always gets a sockaddr structure for the remote UDP peer. The Winsock control internally uses the same sockaddr structure in both sendto and recvfrom API. As UDP is a connectionless protocol, it is possible that one UDP peer could receive a UDP message from a third machine unexpectedly. Therefore, it is important to reset the RemoteHostIP and RemotePort properties to your own known values before calling the SendData method.