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(ByVa l 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(ByVa l 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(ByVa l 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).
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(ByVa
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(ByVa
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(ByVa
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
have you seen this UDP example?
https://www.experts-exchange.com/questions/20346901/UDP-Broadcasting-Question.html
https://www.experts-exchange.com/questions/20346901/UDP-Broadcasting-Question.html
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.
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? ;-)
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.
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.
ASKER
... oops ... hit wrong button and sent double response.
ASKER
That works for now and does not cause large time problems (it does impact throughput, but works). Thanks!
ASKER
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
---
---
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
---
ASKER
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
http://support.microsoft.com/default.aspx?scid=kb;en-us;192564
ASKER
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.
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.
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.
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!