H UdpClient δεν έχει κάποιο πρόβλημα, αντίθετα απ' όσα λέει το thread του 2002 το οποίο παραθέτει ο mixio. Το UdpClient είναι ένα helper class το οποίο διευκολύνει τη χρήση UDP sockets. Χρησιμοποιώντας το χρησιμοποιείς ήδη sockets. Δεν υπάρχει λόγος να αποφύγεις την UdpClient απλά και μόνο επειδή θέλεις να χρησιμοποιήσεις sockets.
Όσον αφορά το πότε πρέπει να καλέσεις τί και που, έφτιαξα στα πολύ γρήγορα ένα πρόγραμμα το οποίο στέλνει UDP πακέτα στην 9091 και τα λαμβάνει ασύγχρονα. Για να το δοκιμάσεις θα πρέπει να το τρέξεις δύο φορές και στο ένα instance να πατήσεις start, στο άλλο να γράψεις ένα μήνυμα και να πατήσεις send. Χρησιμοποιούνται δύο UdpClients. Ο ένας είναι πεδίο της φόρμας αλλά δημιουργείται όταν πατάς το start. Μετά, με την κλήση της BeginReceive περιμένει ένα πακέτο. Όταν το λάβει το εμφανίζει και αρχίζει να ξανακούει. Ο άλλος δημιουργείται μόνο για να σταλεί ένα μήνυμα και μετά καταστρέφεται.
using System;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
namespace UdpChat
{
public partial class ChatForm : Form
{
/// <summary>
/// Η κλάση UdpState χρησιμοποιείται για να περάσουμε στην BeginReceive
/// στοιχεία για το ποιός την καλεί
/// </summary>
public class UdpState
{
/// <summary>
/// Ποιός είναι ο UdpClient ο οποίος δέχτηκε δεδομένα?
/// Μπορεί η ίδια μέθοδος να χρησιμοποιείται από πολλούς UdpClients,
/// οπότε πρέπει να ξεχωρίσουμε για ποιόν είναι τα δεδομένα
/// </summary>
public UdpClient Client{get;set;}
/// <summary>
/// Από ποιά διεύθυνση τα δέχθηκε?
/// </summary>
public IPEndPoint EndPoint { get; set; }
}
//Για συντομία, δουλεύουμε με το 127.0.0.1
IPEndPoint point = new IPEndPoint(IPAddress.Loopback, 9091);
//Ο listener θα δημιουργηθεί μόνο όταν πατηθεί το start
UdpClient listener;
public ChatForm()
{
InitializeComponent();
}
private void startButton_Click(object sender, EventArgs e)
{
//Δημιουργούμε το listener πάνω στο port 9091 αλλά δεν περιορίζουμε
// τη διεύθυνση στην οποία ακούει
if (listener==null)
listener = new UdpClient(9091);
UdpState state=new UdpState{Client=listener,EndPoint=point};
//Αρχίζουμε να ακούμε
listener.BeginReceive(this.ReceiveCallback,state);
this.Text = "Udp Chat - Listening";
}
private void stopButton_Click(object sender, EventArgs e)
{
//Κλείνουμε το listener. Αν έχουμε καλέσει την BeginReceive,
// αυτό θα προκαλέσει μία κλήση στη ReceiveCallback
if (listener != null)
listener.Close();
listener = null;
this.Text = "Udp Chat";
}
private void sendButton_Click(object sender, EventArgs e)
{
//Δημιουργούμε ένα νέο UdpClient για την αποστολή
using (UdpClient client = new UdpClient())
{
//Συνδεόμαστε στον προορισμό
client.Connect(IPAddress.Loopback, 9091);
//Μετατρέπουμε το μήνυμα σε bytes
byte[] message = Encoding.Unicode.GetBytes(messageBox.Text);
//Το στέλνουμε σύγχρονα
client.Send(message, message.Length);
//Κλείνουμε τον UdpClient
client.Close();
messageBox.Text = String.Empty;
}
}
/// <summary>
/// Καλείται όταν λαμβάνονται νέα δεδομένα
/// </summary>
/// <param name="ar"></param>
private void ReceiveCallback(IAsyncResult ar)
{
//Ποιός μας έστειλε δεδομένα?
UdpState s = (UdpState)ar.AsyncState;
UdpClient listener = s.Client;
IPEndPoint endpoint = s.EndPoint;
//Αν η κλήση οφείλεται στο Close, ο UdpClient.Client θα είναι null.
//Διαφορετικά έχουμε νέα δεδομένα και μπορούμε να τα παραλάβουμε
if (listener.Client != null)
{
//Διαβάζουμε τα bytes
byte[] message = listener.EndReceive(ar, ref endpoint);
//Τα μετατρέπουμε σε string
string receiveString = Encoding.Unicode.GetString(message);
//Προσθέτουμε το κείμενο στο discussionBox
SetText(receiveString, message.Length);
//Ξανακούμε για νέα δεδομένα
listener.BeginReceive(this.ReceiveCallback, s);
}
}
delegate void SetTextCallback(string text, int length);
/// <summary>
/// Η ReceiveCallback εκτελείται σε διαφορετικό thread από τη φόρμα. Γι αυτό
/// πρέπει η κλήση της SetText να μεταφερθεί από το thread της ReceiveCallback
/// σε αυτό του thread. Αυτό γίνεται μέσω της Invoke
/// </summary>
/// <param name="text"></param>
/// <param name="length"></param>
private void SetText(string text, int length)
{
if (this.discussionBox.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text, length });
}
else
{
discussionBox.Text += String.Format("\r\n{0} - {1} bytes", text, length);
}
}
}
}
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos