Written Against Pre-release Information
One of the new things that we get with Windows Phone 7 is socket support. While I expected to be able to open sockets to other machines with servers running on them, one thing caught me by surprise; that you can also send communication from phone to phone using UDP. I've got to give credit to Ricky_T for pointing out the presence of this feature and posting a code sample. I wanted to try this out myself. So I made a version of the code sample that would run on both Windows Phone and on the desktop (Silverlight 4 in Out of Browser mode). I was pleasantly surprised too that I was able to open up peer communication between the desktop and phone without a problem. This capability provides a number of solutions for other problems that I've been considering, such as automatic discovery and configuration for communicating with services hosted on a user's local network.
Most of the code used in the desktop and phone version of this example are identical; I've shared some of the same files between projects. From the files that are not shared, the counterparts in the phone and desktop version are still similar. The core of the code is in a class called Peer
. Let's take a look at part of the body of that class.
private string _channelAddress = "224.0.0.1";
private int _channelPort = 3007;
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
private UdpAnySourceMulticastClient _channel;
public void Initialize()
{
_channel = new UdpAnySourceMulticastClient(
IPAddress.Parse(_channelAddress), _channelPort);
}
public void Open()
{
if (_channel == null)
Initialize();
ClientState = ClientStatesEnum.Opening;
_openResult = _channel.BeginJoinGroup((result) =>
{
_channel.EndJoinGroup(result);
ClientState = ClientStatesEnum.Opened;
}, null);
Receive();
}
void Receive()
{
byte[] _receiveBuffer = new byte[1024];
_channel.BeginReceiveFromGroup(_receiveBuffer, 0, _receiveBuffer.Length, (r) =>
{
if(ClientState!=ClientStatesEnum.Closing)
{
try
{
IPEndPoint source;
int size= _channel.EndReceiveFromGroup(r, out source);
OnMessageReceived(_receiveBuffer, size, source);
}
catch (Exception )
{
}
finally
{
this.Receive();
}
}
}, null);
}
public void Send(byte[] data)
{
if(ClientState==ClientStatesEnum.Opened)
{
_channel.BeginSendToGroup(data, 0, data.Length, (r) =>
_channel.EndSendToGroup(r),null);
}
}
This class only sends and receives byte arrays. My only goal here was to see the code work so there are other considerations that I have decided to overlook for now. I made a client to use this code too. The client sends and receives plain text. Before sending a block of text, it is necessary to convert the text to a byte array. The encoding classes in .NET will take care of this for me. When a message comes in, I can also use an encoder to convert the byte array back to a string
.
For this program, I am adding the incoming message to a list along with the IP address from which it came:
void _peer_MessageReceived(object sender, MessageReceivedEventArgs e)
{
Action a = () =>
{
string message = System.Text.UTF8Encoding.Unicode.GetString(e.Data, 0, e.Size);
MessageList.Add(String.Format("{0}:{1}", e.Endpoint.Address.ToString(), message));
OnIncomingMessageReceived(message, e.Endpoint.Address.ToString());
};
if (UIDispatcher == null)
a();
else
UIDispatcher.BeginInvoke(a);
}
public void SendMessage(string message)
{
byte[] encodedMessage= UTF8Encoding.Unicode.GetBytes(message);
_peer.Send(encodedMessage);
}
When the code is run on any combination of multiple phones or computers, message types on any one of the devices appears on all of them. Nice! Now to start making use of it.