You don't need a thread.
Create a UdpClient. Call BeginReceive() on it, passing in a completion function. If BeginReceive returns a result that completed asynchronously, simply spin on BeginReceive() and dispatch the data until there is no more data to send. I've never seen BeginReceive complete synchronously, though.
In the completion function, call EndReceive(), and if it did not complete synchronously, issue another BeginReceive(). Then dispatch the data (byte array) that you received.
For sending, call BeginSend() with a completion function when you have a full packet to send; the completion function doesn't need to do much. In general, you'll want to use a queue of messages, and glom them all together into a single packet to send something like 20 times a second.
Jon Watte, Direct3D MVP
Tweets, occasionallykW X-port 3ds Max .X exporter
kW Animation source code