I need an example for a UDP socket client. Haven't been able to find a relevant one anywhere. I'd like the example to use the System.Net.Sockets.UDPClient control, and be asynchronous, when it comes to receiving data.
Printable View
I need an example for a UDP socket client. Haven't been able to find a relevant one anywhere. I'd like the example to use the System.Net.Sockets.UDPClient control, and be asynchronous, when it comes to receiving data.
There is a thread on this forum that shows that. I'd post one myself, but I haven't actually tested it yet, so it probably doesn't work yet. Maybe tomorrow, but I borrowed much of it from a thread here.
I can't seem to find that thread. Can you provide me with a link?
This thread is particularly interesting:
http://www.vbforums.com/showthread.p...ight=UDPClient
I found the same thing, you can't receive with UDPClient, and have to use a socket instead. I was just looking at my code, and found that I had implemented that. Still looking for the post I mentioned, though.
Here's another interesting point:
http://www.vbforums.com/showthread.p...ight=UDPClient
Ok, this is the thread I was talking about:
http://www.vbforums.com/showthread.p...ight=UDPClient
Looks like UDP is a perpetual problem. I should be diving into it tomorrow, as my code is about ready to be tested. Should be interesting, since most everybody is having one problem or another with it.
I am so glad you take it that way! :) I hope this will somehow lead to a solution.
Wouldnt this work? I have no ability to test it since I have nowhere to connect the udp client.
VB Code:
Private uClient As Net.Sockets.UdpClient Private thrRead As Threading.Thread Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click uClient = New Net.Sockets.UdpClient("host", 0) thrRead = New Threading.Thread(AddressOf DoRead) thrRead.Start() End Sub Private Sub DoRead() Dim byteArray(uClient.Client.ReceiveBufferSize) As Byte Do uClient.Client.Receive(byteArray, uClient.Client.ReceiveBufferSize, Net.Sockets.SocketFlags.None) MessageBox.Show(System.Text.Encoding.Default.GetString(byteArray)) Loop End Sub
the uClient.Client.Recieve() method is not asynchronous, so it will block the current thread until it recieves something.
I will try that tomorrow, and report if it worked. Thank you.
According to the first link I provided, UDPClient.Receive is buggy, and won't work.
Yeah but does that also apply to the Recieve method of the underlying socket? Im calling UDPClient.Client.Recieve (UDPClient.Client references to the underlying socket), dont know if that works better/worse.Quote:
Originally Posted by Shaggy Hiker
You guys are cool. I can't believe you're actually doing all this just for me. VBForums for the wind!
Sorry, I missed that point. You are right, that should work. I'm trying that route myself.Quote:
Originally Posted by Atheist
And about sending things? Does that work? And can you provide me with an example? Also, did anyone figure out if Atheist's code would work? I can't try it myself before monday.
Ok, I tested my program, and it works...so far. Sending and receiving is fine, but I still have one issue with delegates to work out since the messages come in on one thread. Here's the whole class, minus some stuff that is specific to this application:
vb Code:
Public Class UDPControl Implements IDisposable 'The UDP listening components. Private WithEvents locUDPIn As System.Net.Sockets.Socket Private WithEvents locUDPOut As System.Net.Sockets.UdpClient Private WithEvents myTimer As System.Windows.Forms.Timer Private ThreadListen As New Threading.Thread(AddressOf WaitForPending) Private timeCount As Integer 'A few endpoints. Private mRobot As System.Net.IPEndPoint Private mRegistered As Boolean Private mIAsynch As IAsyncResult 'The dataQueue Private dgList As Queue(Of DataGramReceiver) 'The result Private callType As Integer Private disposedValue As Boolean = False ' To detect redundant calls Public Shared TheUDP As New UDPControl 'The only instance of the Singleton class. #Region "Constructors and Destructors" Private Sub New() 'Set up the queue. dgList = New Queue(Of DataGramReceiver) locUDPIn = New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Dgram, Net.Sockets.ProtocolType.Udp) locUDPOut = New System.Net.Sockets.UdpClient(11012) locUDPIn.Bind((New Net.IPEndPoint(Net.IPAddress.Any, 11011))) locUDPOut.EnableBroadcast = True callType = 0 myTimer = New System.Windows.Forms.Timer myTimer.Interval = 1000 myTimer.Enabled = False 'Start listening. ThreadListen.Name = "Listening Thread" ThreadListen.Start() End Sub ' IDisposable Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposedValue Then If disposing Then locUDPOut.Close() myTimer.Dispose() ThreadListen.Abort() locUDPIn.Close() End If ' TODO: free shared unmanaged resources End If Me.disposedValue = True End Sub #Region " IDisposable Support " ' This code added by Visual Basic to correctly implement the disposable pattern. Public Sub Dispose() Implements IDisposable.Dispose ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. Dispose(True) GC.SuppressFinalize(Me) End Sub #End Region #End Region #Region "Properties and Events" Public Event DataReceived(ByVal senType As Integer) Public Event ReceiveFailed() Private Event DataIn() #End Region #Region "Public Functions" Public Sub CallSynchTime() Dim buff(0) As Byte Dim broad As New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, 11012) buff(0) = 100 'Nothing but a single byte. locUDPOut.Send(buff, 1, broad) End Sub #End Region #Region "Private Functions" Private Sub WaitForPending() Dim bHolder(99) As Byte Dim numIn As Integer Dim dgRec As DataGramReceiver Dim bTrans() As Byte While True If CBool(locUDPIn.Available) Then numIn = locUDPIn.Receive(bHolder, 100, Net.Sockets.SocketFlags.None) If numIn > 4 Then If bHolder(0) = 128 Then 'Set the holder up to the right size. ReDim bTrans(numIn - 3) Array.Copy(bHolder, 2, bTrans, 0, numIn - 2) dgRec = New DataGramReceiver(bHolder(3), bTrans) Add(dgRec) ElseIf bHolder(0) = 100 Then ReDim bTrans(numIn - 2) Array.Copy(bHolder, 1, bTrans, 0, numIn - 2) dgRec = New DataGramReceiver(100, bTrans) Add(dgRec) End If End If End If End While End Sub 'Adds one DGR to the queue Private Sub Add(ByVal dgR As DataGramReceiver) 'Set the monitor. Threading.Monitor.Enter(Me) Try dgList.Enqueue(dgR) 'That's all that happens. Finally Threading.Monitor.Exit(Me) End Try End Sub #End Region End Class
I stripped out much of the code for specific messages. What I tested was that on computer 1 I had this code running, and called SynchTime. On computer 2 I had a similar class that returned the time on system 2. When I called SynchTime on System 1, the message was received on System 2, which sent back the time. The time is preceeded by the value 100, so if you look in the WaitForPending function, you will see where the first value in the byte array is used to do different things. This is just in there so that I can sort the incoming messages, and it is largely irrelevant to UDP.
One issue I have not yet resolved is that messages come in on the listening thread. You will see the Private Function Add, which uses a Monitor to get ahold of a Queue, and add an item to it. The main thread will then have to get items off the Queue. I can do this via polling, but I want to have the one thread raise an event on the other, and I haven't got that working yet.
Oh yeah, I should also note that I am using two different sockets. This program is not truly bi-directional, as all remote systems will be sending to one central receiver, which will be broadcasting to all listeners. I was somewhat concerned that there might be too much traffic on one line, so I am using one port as Input, and another port as Output. Each need only listen to one, and talk on the other. I have no idea whether or not this is a superior solution, but none of the client systems will ever need to listen on the same line they will be talking on, and many may NEVER need to listen on the talking port (they will be downstream only nodes). Meanwhile, the Server (brainstem in this case, since it is a robot) will ONLY listen to the listening line, as it will NEVER have to make a request to any of the other systems.
Uuuuh thank you so much for that! Thanks! Really appreciated! However, can you try to make it standalone, so it won't support multiple clients, and not support server? I only need a client, that needs to be able to send and receive data.Quote:
Originally Posted by Shaggy Hiker
What's the difference between what I have and that? UDP is never really a server, this is just a variation where one port is listened to, and a different port is written to. You could use this exact same code if you change the port numbers to be the same. The UDPClient won't Read correctly (a bug exists, as noted in one of the earlier threads), so I had to use a socket for the receiving, while I still used the UDPClient to send data. I connected them to two different ports, but you could just as easily use the same port number (I use 11011 and 11012) for both, and it would work the same. One object would read, and a different object would write, but if it was on the same port, it should be fine.
I know, but I'd like it more simply. I need to learn something as well, you know... And well, your code is extremely useful, but yet, very hard to understand. I am not that good at programming yet. If you can make it kind of like the one Atheist made, that would be awesome!
So why not use what Atheist provided? They are essentially the same thing in that a thread is started that reads from the UDPClient. I'm not sure what difference there is. Sending via a UDP is much easier than reading, which is why it is such a tiny piece of my code. All you do is make up a byte array with the bytes you want to send, and call the UDPClient.Send() method. What you put in that byte array is a whole different question, and not related to UDP.
I'm glad you care.
Here: http://www.vbforums.com/showthread.p...03#post2934803
I don't undertstand your point. Atheist posted simpler code, I posted a more complicated and thorough example. There was an intermediate version in the third link I provided back in post#4. Are none of these suitable for your situation, and why not?
The client only needs to be able to receive and send data. Also, I don't want it to be able to be a server.
That's what Atheists code does. There's no simpler solution, which presents some issues that he didn't address. The issues are these:
1) To send, you would just use the UDPClient.Send method, but that sends an array of bytes. Since you rarely will actually work with an array of bytes, there is some manipulation required to take whatever you want to send, and turn it into an array of bytes. If all you are sending is a string, that's not very hard: System.Text.Encoding.ASCII.GetBytes() should be the method (I didn't look it up, but it would be really close).
2) The other issue related to sending is whether or not you want to broadcast, or send to a specific IP endpoint. In my example, I used broadcast, which I frankly think is easier, but in my case I HAD to use broadcast because of the program design. The problem with sending to a specific IP address is that you have to know the address, which you may or may not.
3) The issue with receiving data is only that receiving pretty much has to happen on a separate thread, as Atheist and I both have. The reason for this is that receiving occurs in a tight, perpetual, loop. If you receive in your main thread, it will block until something is received, though you could set it up so that it just listens for a second or two, then goes on, but this would mean you would miss messages. Therefore, a thread for receiving is pretty nearly the ONLY way you will ever see that implemented. The problem with a thread is that you then have to deal with getting information back to the main thread. Atheists code solved that by never sending information out of the thread (he just posted it in a messagebox). If you want to display it in a control in the UI thread, you can use Invoke or BeginInvoke on the control. If you just want to raise an event in the main thread, I have a way to do that, and it's simple, but I'll let you decide what you want to do.
This works, but how to send data as simple as this too? Good job Atheist.Quote:
Originally Posted by Atheist
Sending is even easier:
vb Code:
Public Sub SendString(st1 as string) Dim buff() As Byte Dim broad As New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, 11012) buff() = system.text.encoding.ASCII.GetBytes(st1) UClient.Send(buff, 1, broad) End Sub
where 11012 is the port number, which is something you can decide on. Use whatever you like, but a bunch of the low numbers are reserved, and there's an upper limit, though I forget what it is.
I just corrected your code! So full of mistakes? You mean something like this?
Public Sub Send(ByVal Data As String)
Dim Buffer() As Byte
Dim Broad As New System.Net.IPEndPoint(System.Net.IPAddress.Broadcast, 4898)
Buffer = System.Text.Encoding.ASCII.GetBytes(Data)
Client.Send(Buffer, Buffer.Length, Broad)
End Sub
Even the one I corrected doesn't work. Atheist, any solution for this?
Or maybe it's actually the DoRead sub that doesn't work? I can't tell!
vb Code:
Private Sub DoRead(ByVal Port As Integer) Dim ByteArray(Client.Client.ReceiveBufferSize) As Byte Do Client.Client.Receive(byteArray, Client.Client.ReceiveBufferSize, Net.Sockets.SocketFlags.None) RaiseEvent ClientReceived(System.Text.Encoding.Default.GetString(ByteArray)) Loop End Sub Public Sub Send(ByVal Data As String) Dim Buffer() As Byte Buffer = System.Text.Encoding.ASCII.GetBytes(Data) Client.Send(Buffer, Buffer.Length) End Sub
Should they both work?
You'd have to show more code, you've made sufficient changes to the code Atheist originally posted that the actual source of the problem you are having cannot be determined. Perhaps you have the ports set wrong, perhaps you are trying to raise an event on a background thread and expecting something good to happen in a different thread.
But since you have gotten downright rude at this point, I might as well step out.
Okay. I hereby show my whole class.Quote:
Originally Posted by Shaggy Hiker
vb Code:
Public Class Client Private Client As Net.Sockets.UdpClient Public Event ClientReceived(ByVal Data As String) Public Event ClientError(ByVal Number As Long, ByVal Description As String) Public Event ServerConnected() Public ReadOnly Property Socket() As Net.Sockets.UdpClient Get Return Client End Get End Property Public Sub Connect(ByVal HostName As String, ByVal Port As Integer) If Port = 0 Then Err.Raise(44, "MathyProductions.Sockets.TCP.Client", "The port to connect to was not set") End If If HostName = vbNullString Then Err.Raise(45, "MathyProductions.Sockets.TCP.Client", "The hostname to connect to was not set") End If Client = New Net.Sockets.UdpClient(HostName, Port) DoRead End Sub Private Sub DoRead() Dim ByteArray(Client.Client.ReceiveBufferSize) As Byte Do Client.Client.Receive(byteArray, Client.Client.ReceiveBufferSize, Net.Sockets.SocketFlags.None) RaiseEvent ClientReceived(System.Text.Encoding.Default.GetString(ByteArray)) Loop End Sub Public Sub Send(ByVal Data As String) Dim Buffer() As Byte Buffer = System.Text.Encoding.ASCII.GetBytes(Data) Client.Send(Buffer, Buffer.Length) End Sub End Class
Nevermind, got the code working. DoRead was not even called! *LOL*
Hi Shaggy . . .
I would greatly appreciate if you could post or, considering the circumstances of this thread, pm the DataGramReceiver code.
Many Thanks
Mike
Should this thread be in "Network Programming"?
I don't have it handy, but I can add it to this thread later today or tomorrow. What is it that you are looking for, though? My datagramreceiver is somewhat custom. The first two bytes have special meaning, while the remainder of the bytes are one of a variety of structures that have been serialized to a binary stream. Of those first two bytes, one indicates the origin of the message, while the second one tells what type of message has been serialized.Quote:
Originally Posted by MikkyThomeon
Are you looking for an example of serializing/deserializing the structures? I think I got that from something Atheist posted, but I could post the way I handle it.
oops - Thanks I have figured something out to do the trick. In fact the mulit threading is not even needed - I have a barcode scanning program that uses UDP as a server to send an instruction to multiple viewer clients on the network so that the clients are updated whenever an item is scanned from one of the warehouses.
I was initially thinking of sending the data directly to the clients but its far more reliable to save the data from teh scanning application directly to the database by odbc and then broadcast the event.
Thanks anyway :)
Mike
When I try out the code that Shaggy Hiker made, I only get
"DataGramReceiver' is not defined."
What can I do about this? I really need this to work!
DataGramReceiver is a custom class that SH wrote himself, I'm sure he'll let you know how it works if you PM him.