私は数日間、Windows Phone 7に関するさまざまなフォーラムを見てきましたが、決定的な答えは得られませんでした。これまでのところ、wifi経由で接続されたコンピューターからWindows Phone 7デバイス(エミュレーターで実行)に送信されたUDPパケット(ブロードキャストでもユニキャストでもない)を受信できませんでした。
どうやらUDPユニキャストがサポートされているはずであり、以下のコードは正しく実行されますが、UDPパケットは電話から受信されません。誰かが以下のコードを修正できることを願っています。
以下のコードは、他のフォーラムでこれまでに提供されたすべての提案に従っていることに注意してください。
- 最初に目的の宛先にパケットを送信してから、応答をリッスンします
- ブロードキャストではなくUDPユニキャストを使用します(両方の設定をテストできますisBroadcast変数)
- SilverLightで許可されているポート4502を使用する
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();
}
}
}
}