3
votes

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:

  1. Send a packet first to the intended destination and then listen for reply
  2. Do not use broadcast but UDP unicast (I can test both setting isBroadcast variable)
  3. 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();
  }
  }
}
}
1
Are you running it on a device, or an emulator?Antimony

1 Answers

0
votes

Port 4502 is not required in Silverlight for Windows Phone but only on Silverlight applications for the browser.. I'm checking your code because I have the same problem.