I have watched for several days the various forums regarding Windows Phone 7 however none gave me a definitive answer. So far I have not been able to receive a UDP packet (neither broadcast nor unicast) sent from a computer connected over wifi to a Windows Phone 7 device (running on emulator).
Apparently UDP unicast should be supported and the code below runs correctly, however no UDP packet is received form the phone. I hope someone can correct the below code.
Notice the below code follows all suggestions so far given on other forums, namely:
- Send a packet first to the intended destination and then listen for reply
- Do not use broadcast but UDP unicast (I can test both setting isBroadcast variable)
- Use port 4502 which is permitted by SilverLight
MainPage.xaml
<phone:PhoneApplicationPage
x:Class="UDPClient.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="UDP Socket Application" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="Client" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,-8,12,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<!-- Fit to content -->
<ColumnDefinition Width="Auto"/>
<!-- Fit to content -->
<ColumnDefinition Width="Auto"/>
<!-- Fit to content -->
<ColumnDefinition Width="*"/>
<!-- Take up remaining space -->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- Fit to content -->
<RowDefinition Height="Auto"/>
<!-- Fit to content -->
<RowDefinition Height="Auto"/>
<!-- Fit to content -->
<RowDefinition Height="*"/>
<!-- Take up remaining space -->
</Grid.RowDefinitions>
<!-- Grid Row 0: Remote Host Input Field >-->
<TextBlock Grid.Row="0" Grid.Column="0" Text="Host Name:"
VerticalAlignment="Center" HorizontalAlignment="Center"
FontSize="{StaticResource PhoneFontSizeNormal}" />
<TextBox x:Name="txtRemoteHost" Grid.Row="0" Grid.Column="1" Height="70" Width="200"
VerticalAlignment="Top" HorizontalAlignment="Left"
FontSize="{StaticResource PhoneFontSizeNormal}" Text="192.168.1.3" />
<!-- Grid Row 1: Echo >-->
<!-- TextBlock for Echo command label-->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Text To Echo:"
VerticalAlignment="Center" HorizontalAlignment="Center"
FontSize="{StaticResource PhoneFontSizeNormal}" />
<!-- TextBox for Echo command text input-->
<TextBox x:Name="txtInput" Grid.Row="1" Grid.Column="1" Height="70" Width="200"
VerticalAlignment="Top" HorizontalAlignment="Left"
FontSize="{StaticResource PhoneFontSizeNormal}" Text="test..." />
<!-- Button to the right of the input textbox for the Echo command >-->
<Button x:Name="btnEcho" Grid.Row="1" Grid.Column="2" Height="70" Width="120"
Content="Echo"
FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnEcho_Click"/>
<!-- Grid Row 2: Quote of the Day-->
<!-- Button for the Quote command >-->
<Button x:Name="btnGetQuote" Grid.Row="2" Grid.ColumnSpan="4" Height="70"
Content="Get Quote of the Day"
FontSize="{StaticResource PhoneFontSizeNormal}" Click="btnGetQuote_Click"/>
<!-- Grid Row 3: Output-->
<!-- Output TextBox named 'txtOutput' >-->
<TextBox x:Name="txtOutput" Grid.Row="3" Grid.ColumnSpan="4" Background="Black" BorderBrush="Green"
AcceptsReturn="False" Foreground="LightGray" FontFamily="Courier New"
IsHitTestVisible="False" FontSize="{StaticResource PhoneFontSizeSmall}" TextWrapping="Wrap" />
<Button Content="Listen" Grid.Column="1" Grid.ColumnSpan="2" Height="70" HorizontalAlignment="Left" Margin="195,0,0,0" Name="Listenbutton" VerticalAlignment="Top" Width="125" Click="Listenbutton_Click" />
</Grid>
</Grid>
<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="MenuItem 1"/>
<shell:ApplicationBarMenuItem Text="MenuItem 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Net.Sockets;
using System.Threading;
namespace UDPClient
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
// Constants
const int ECHO_PORT = 7; // The Echo protocol uses port 7 in this sample
const int QOTD_PORT = 17; // The Quote of the Day (QOTD) protocol uses port 17 in this sample
const int UDP_PORT = 4502;
/// <summary>
/// Handle the btnEcho_Click event by sending text to the echo server and outputting the response
/// </summary>
private void btnEcho_Click(object sender, RoutedEventArgs e)
{
// Clear the log
ClearLog();
// Make sure we can perform this action with valid data
if (ValidateRemoteHost() && ValidateInput())
{
// Instantiate the SocketClient
SocketClient client = new SocketClient();
SocketAsyncEventArgs socketEventArg;
// Attempt to send our message to be echoed to the echo server
Log(String.Format("Sending '{0}' to server ...", txtInput.Text), true);
string result = client.Send(txtRemoteHost.Text, ECHO_PORT, txtInput.Text, false, out socketEventArg);
Log(result, false);
// Receive a response from the echo server
Log("Requesting Receive ...", true);
result = client.UDPReceive(ECHO_PORT, false);
Log(result, false);
// Close the socket connection explicitly
client.Close();
}
}
private void Listenbutton_Click(object sender, RoutedEventArgs e)
{
// Clear the log
ClearLog();
// Make sure we can perform this action with valid data
if (ValidateRemoteHost())
{
// Instantiate the SocketClient
SocketClient client = new SocketClient();
// Receive packets
string result = client.UDPReceive(UDP_PORT, false);
Log(result, false);
// Close the socket connection explicitly
client.Close();
}
}
/// <summary>
/// Handle the btnGetQuote_Click event by receiving text from the Quote of the Day (QOTD) server and outputting the response
/// </summary>
private void btnGetQuote_Click(object sender, RoutedEventArgs e)
{
// Clear the log
ClearLog();
// Receive response from the QOTD server
Log("nothing...", true);;
}
}
#region UI Validation
/// <summary>
/// Validates the txtInput TextBox
/// </summary>
/// <returns>True if the txtInput TextBox contains valid data, False otherwise</returns>
private bool ValidateInput()
{
// txtInput must contain some text
if (String.IsNullOrWhiteSpace(txtInput.Text))
{
MessageBox.Show("Please enter some text to echo");
return false;
}
return true;
}
/// <summary>
/// Validates the txtRemoteHost TextBox
/// </summary>
/// <returns>True if the txtRemoteHost contains valid data, False otherwise</returns>
private bool ValidateRemoteHost()
{
// The txtRemoteHost must contain some text
if (String.IsNullOrWhiteSpace(txtRemoteHost.Text))
{
MessageBox.Show("Please enter a host name");
return false;
}
return true;
}
#endregion
#region Logging
/// <summary>
/// Log text to the txtOutput TextBox
/// </summary>
/// <param name="message">The message to write to the txtOutput TextBox</param>
/// <param name="isOutgoing">True if the message is an outgoing (client to server) message, False otherwise</param>
/// <remarks>We differentiate between a message from the client and server
/// by prepending each line with ">>" and "<<" respectively.</remarks>
private void Log(string message, bool isOutgoing)
{
string direction = (isOutgoing) ? ">> " : "<< ";
txtOutput.Text += Environment.NewLine + direction + message;
}
/// <summary>
/// Clears the txtOutput TextBox
/// </summary>
private void ClearLog()
{
txtOutput.Text = String.Empty;
}
#endregion
}
}
SocketClient.cs
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace UDPClient
{
public class SocketClient
{
// Cached Socket object that will be used by each call for the lifetime of this class
Socket _socket = null;
// Signaling object used to notify when an asynchronous operation is completed
static ManualResetEvent _clientDone = new ManualResetEvent(false);
// Define a timeout in milliseconds for each asynchronous call. If a response is not received within this
// timeout period, the call is aborted.
const int TIMEOUT_MILLISECONDS = 1000;
// The maximum size of the data buffer to use with the asynchronous socket methods
const int MAX_BUFFER_SIZE = 2048;
bool isHasSent = false;
int errorCode = 0;
/// <summary>
/// SocketClient Constructor
/// </summary>
public SocketClient()
{
// The following creates a socket with the following properties:
// AddressFamily.InterNetwork - the socket will use the IP version 4 addressing scheme to resolve an address
// SocketType.Dgram - a socket that supports datagram (message) packets
// PrototcolType.Udp - the User Datagram Protocol (UDP)
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
/// <summary>
/// Send the given data to the server using the established connection
/// </summary>
/// <param name="serverName">The name of the server</param>
/// <param name="portNumber">The number of the port over which to send the data</param>
/// <param name="data">The data to send to the server</param>
/// <returns>The result of the Send request</returns>
public string Send(string serverName, int portNumber, string data, bool isBroadcast, out SocketAsyncEventArgs socketEventArg)
{
string response = "Operation Timeout";
// Create SocketAsyncEventArgs context object
// We are re-using the _socket object that was initialized in the Connect method
if (_socket != null)
{
socketEventArg = new SocketAsyncEventArgs();
// Set properties on context object
System.Diagnostics.Debug.WriteLine("Send(): setting remoteEndPoint");
if (isBroadcast)
socketEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Broadcast, portNumber);
else
socketEventArg.RemoteEndPoint = new DnsEndPoint(serverName, portNumber);
System.Diagnostics.Debug.WriteLine("Send(): remoteEndPoint correctly set");
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
response = e.SocketError.ToString();
// Unblock the UI thread
_clientDone.Set();
isHasSent = true;
});
// Add the data to be sent into the buffer
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
// Make an asynchronous Send request over the socket
_socket.SendToAsync(socketEventArg);
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
socketEventArg = null;
response = "Socket is not initialized";
}
return response;
}
public String UDPReceive(int portNumber, bool isBroadcast)
{
SocketAsyncEventArgs socketEventArg;
System.Diagnostics.Debug.WriteLine("calling Send(\"server\", portNumber, \" \", isBroadcast, out socketEventArg)");
Send("servern", portNumber, " ", !isBroadcast, out socketEventArg);
Thread.Sleep(1000);
while (!isHasSent)
{
Thread.Sleep(1);
}
System.Diagnostics.Debug.WriteLine("calling Receive(portNumber, isBroadcast, socketEventArg)");
return Receive(portNumber, isBroadcast, out socketEventArg);
}
/// <summary>
/// Receive data from the server
/// </summary>
/// <param name="portNumber">The port on which to receive data</param>
/// <returns>The data received from the server</returns>
public string Receive(int portNumber, bool isBroadcast, out SocketAsyncEventArgs socketEventArg)
{
string response = "Operation Timeout";
// We are receiving over an established socket connection
if (_socket != null)
{
// Create SocketAsyncEventArgs context object
socketEventArg = new SocketAsyncEventArgs();
System.Diagnostics.Debug.WriteLine("Receive(): setting remoteEndPoint");
if (isBroadcast)
socketEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Broadcast, portNumber);
else
socketEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Any, portNumber);
System.Diagnostics.Debug.WriteLine("Receive(): remoteEndPoint correctly set");
// Setup the buffer to receive the data
socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);
System.Diagnostics.Debug.WriteLine("Receive(): SetBuffer() correctly called");
// Inline event handler for the Completed event.
// Note: This even handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
System.Diagnostics.Debug.WriteLine("Receive(): SocketError.Success");
// Retrieve the data from the buffer
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
}
else
{
System.Diagnostics.Debug.WriteLine("Receive(): SocketError.Error");
response = e.SocketError.ToString();
}
System.Diagnostics.Debug.WriteLine("Receive(): Set()");
_clientDone.Set();
});
System.Diagnostics.Debug.WriteLine("Receive(): Reset()");
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
try
{
// Make an asynchronous Receive request over the socket
_socket.ReceiveFromAsync(socketEventArg);
}
catch (SocketException sockEx)
{
Console.WriteLine(sockEx.Message);
Console.WriteLine(sockEx.ErrorCode);
Console.WriteLine(sockEx.StackTrace);
Console.ReadLine();
System.Diagnostics.Debug.WriteLine("errorCode=" + errorCode + " " + sockEx.Message + sockEx.ErrorCode + sockEx.StackTrace);
errorCode = 11;
response += "errorCode=" + errorCode + " " + sockEx.Message + sockEx.ErrorCode + sockEx.StackTrace;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
Console.ReadLine();
System.Diagnostics.Debug.WriteLine("errorCode="+errorCode+" "+ex.Message + ex.StackTrace);
errorCode = 22;
response += "errorCode="+errorCode+" "+ex.Message + ex.StackTrace;
}
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
System.Diagnostics.Debug.WriteLine("Receive(): _clientDone.WaitOne(TIMEOUT_MILLISECONDS)");
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
socketEventArg = null;
response = "Socket is not initialized";
}
System.Diagnostics.Debug.WriteLine("Receive(): response = " + response);
return response;
}
/// <summary>
/// Closes the Socket connection and releases all associated resources
/// </summary>
public void Close()
{
if (_socket != null)
{
_socket.Close();
}
}
}
}