Одной из особенностей, которые меня взволновали в Windows Phone 8 SDK, являются API-интерфейсы Proximity. Эти API предоставляют вашему приложению доступ к Bluetooth-соединению и Wi-Fi Direct / NFC для совместного использования всего, что вы хотите, с 2-х совместимых телефонов, на которых запущено приложение, ожидающее соединения. Мой пример довольно прост и не подходит для реальной жизни, но это хорошо для начинающих.
В этом примере мы будем интенсивно использовать класс PeerFinder, который предоставляет функции для принятия и ожидания подключений и асинхронного поиска одноранговых узлов для подключения. Как только соединение установлено, мы можем читать и писать, используя двоичные устройства чтения через сокеты. Довольно легко!
MainPage.xaml.cs using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Windows.Foundation; using Windows.Networking.Proximity; using Windows.Networking.Sockets; using Windows.Storage.Streams; namespace PhoneApp1 { public partial class MainPage : PhoneApplicationPage { private IReadOnlyList<PeerInformation> _peerInformationList; private PeerInformation _requestingPeer; private StreamSocket _socket = null; private bool _socketClosed = true; private DataWriter _dataWriter; private DataReader _dataReader; private bool _triggeredConnectSupported = false; bool _isLaunchedByTap = false; private bool _browseConnectSupported = false; // Constructor public MainPage() { InitializeComponent(); Loaded += MainPage_Loaded; } void MainPage_Loaded(object sender, RoutedEventArgs e) { _triggeredConnectSupported = (PeerFinder.SupportedDiscoveryTypes & PeerDiscoveryTypes.Triggered) == PeerDiscoveryTypes.Triggered; _browseConnectSupported = (PeerFinder.SupportedDiscoveryTypes & PeerDiscoveryTypes.Browse) == PeerDiscoveryTypes.Browse; if (_triggeredConnectSupported || _browseConnectSupported) { // This scenario demonstrates "PeerFinder" to browse for peers to connect to using a StreamSocket PeerFinder_StartFindingPeersButton.Click += new RoutedEventHandler(PeerFinder_StartFindingPeers); PeerFinder_BrowsePeersButton.Click += new RoutedEventHandler(PeerFinder_BrowsePeers); PeerFinder_ConnectButton.Click += new RoutedEventHandler(PeerFinder_Connect); PeerFinder_AcceptButton.Click += new RoutedEventHandler(PeerFinder_Accept); PeerFinder_SendButton.Click += new RoutedEventHandler(PeerFinder_Send); PeerFinder_StartFindingPeersButton.Visibility = Visibility.Visible; } } string[] rgConnectState = {"PeerFound", "Listening", "Connecting", "Completed", "Canceled", "Failed"}; public void NotifyUser(string strMessage, NotifyType type) { Dispatcher.BeginInvoke( () => { switch (type) { // Use the status message style. case NotifyType.StatusMessage: MessageBox.Show(strMessage,"Status",MessageBoxButton.OK); break; // Use the error message style. case NotifyType.ErrorMessage: MessageBox.Show(strMessage,"Error",MessageBoxButton.OK); break; } }); } private void TriggeredConnectionStateChangedEventHandler(object sender, TriggeredConnectionStateChangedEventArgs eventArgs) { if (eventArgs.State == TriggeredConnectState.PeerFound) { // Use this state to indicate to users that the tap is complete and // they can pull there devices away. NotifyUser("Tap complete, socket connection starting!", NotifyType.StatusMessage); } if (eventArgs.State == TriggeredConnectState.Completed) { NotifyUser("Socket connect success!", NotifyType.StatusMessage); // Grab the socket that just connected. _socket = eventArgs.Socket; Dispatcher.BeginInvoke(() => { this.PeerFinder_StartSendReceive(); }); } if (eventArgs.State == TriggeredConnectState.Failed) { NotifyUser("Socket connect failed!", NotifyType.ErrorMessage); } } private bool _peerFinderStarted = false; private void SocketError(String errMessage) { NotifyUser(errMessage, NotifyType.ErrorMessage); PeerFinder_StartFindingPeersButton.Visibility = Visibility.Visible; if (_browseConnectSupported) { PeerFinder_BrowsePeersButton.Visibility = Visibility.Visible; } PeerFinder_SendButton.Visibility = Visibility.Collapsed; PeerFinder_MessageBox.Visibility = Visibility.Collapsed; if (!_socketClosed) { _socketClosed = true; _socket.Dispose(); _socket = null; } } async private void PeerFinder_Send(object sender, RoutedEventArgs e) { NotifyUser("", NotifyType.ErrorMessage); String message = PeerFinder_MessageBox.Text; PeerFinder_MessageBox.Text = ""; // clear the input now that the message is being sent. if (!_socketClosed) { if (message.Length > 0) { try { uint strLength = _dataWriter.MeasureString(message); _dataWriter.WriteUInt32(strLength); _dataWriter.WriteString(message); uint numBytesWritten = await _dataWriter.StoreAsync(); if (numBytesWritten > 0) { NotifyUser("Sent message: " + message + ", number of bytes written: " + numBytesWritten, NotifyType.StatusMessage); } else { SocketError("The remote side closed the socket"); } } catch (Exception err) { if (!_socketClosed) { SocketError("Failed to send message with error: " + err.Message); } } } else { NotifyUser("Please type a message", NotifyType.ErrorMessage); } } else { SocketError("The remote side closed the socket"); } } async private void PeerFinder_Accept(object sender, RoutedEventArgs e) { NotifyUser("Connecting to " + _requestingPeer.DisplayName + "....", NotifyType.StatusMessage); PeerFinder_AcceptButton.Visibility = Visibility.Collapsed; try { _socket = await PeerFinder.ConnectAsync(_requestingPeer); NotifyUser("Connection succeeded", NotifyType.StatusMessage); PeerFinder_StartSendReceive(); } catch (Exception err) { NotifyUser("Connection to " + _requestingPeer.DisplayName + " failed: " + err.Message, NotifyType.ErrorMessage); } } private void PeerConnectionRequested(object sender, ConnectionRequestedEventArgs args) { _requestingPeer = args.PeerInformation; Dispatcher.BeginInvoke( () => { NotifyUser("Connection requested from peer " + args.PeerInformation.DisplayName, NotifyType.StatusMessage); this.PeerFinder_AcceptButton.Visibility = Visibility.Visible; this.PeerFinder_SendButton.Visibility = Visibility.Collapsed; this.PeerFinder_MessageBox.Visibility = Visibility.Collapsed; }); } async void PeerFinder_StartReader() { try { uint bytesRead = await _dataReader.LoadAsync(sizeof(uint)); if (bytesRead > 0) { uint strLength = (uint)_dataReader.ReadUInt32(); bytesRead = await _dataReader.LoadAsync(strLength); if (bytesRead > 0) { String message = _dataReader.ReadString(strLength); NotifyUser("Got message: " + message, NotifyType.StatusMessage); PeerFinder_StartReader(); // Start another reader } else { SocketError("The remote side closed the socket"); } } else { SocketError("The remote side closed the socket"); } } catch (Exception e) { if (!_socketClosed) { SocketError("Reading from socket failed: " + e.Message); } } } // Start the send receive operations void PeerFinder_StartSendReceive() { PeerFinder_SendButton.Visibility = Visibility.Visible; PeerFinder_MessageBox.Visibility = Visibility.Visible; // Hide the controls related to setting up a connection PeerFinder_ConnectButton.Visibility = Visibility.Collapsed; PeerFinder_AcceptButton.Visibility = Visibility.Collapsed; PeerFinder_FoundPeersList.Visibility = Visibility.Collapsed; PeerFinder_BrowsePeersButton.Visibility = Visibility.Collapsed; PeerFinder_StartFindingPeersButton.Visibility = Visibility.Collapsed; _dataReader = new DataReader(_socket.InputStream); _dataWriter = new DataWriter(_socket.OutputStream); _socketClosed = false; PeerFinder_StartReader(); } async void PeerFinder_Connect(object sender, RoutedEventArgs e) { NotifyUser("", NotifyType.ErrorMessage); PeerInformation peerToConnect = null; try { // If nothing is selected, select the first peer if (PeerFinder_FoundPeersList.SelectedIndex == -1) { peerToConnect = _peerInformationList[0]; } else { peerToConnect = _peerInformationList[PeerFinder_FoundPeersList.SelectedIndex]; } NotifyUser("Connecting to " + peerToConnect.DisplayName + "....", NotifyType.StatusMessage); _socket = await PeerFinder.ConnectAsync(peerToConnect); NotifyUser("Connection succeeded", NotifyType.StatusMessage); PeerFinder_StartSendReceive(); } catch (Exception err) { NotifyUser("Connection to " + peerToConnect.DisplayName + " failed: " + err.Message, NotifyType.ErrorMessage); } } async void PeerFinder_BrowsePeers(object sender, RoutedEventArgs e) { NotifyUser("Finding Peers...", NotifyType.StatusMessage); try { _peerInformationList = await PeerFinder.FindAllPeersAsync(); } catch (Exception ex) { Debug.WriteLine("FindAll throws exception" + ex.Message); } Debug.WriteLine("Async operation completed"); NotifyUser("No peers found", NotifyType.StatusMessage); try{ if (_peerInformationList.Count > 0) { PeerFinder_FoundPeersList.Items.Clear(); for (int i = 0; i < _peerInformationList.Count; i++) { ListBoxItem item = new ListBoxItem(); item.Content = _peerInformationList[i].DisplayName; PeerFinder_FoundPeersList.Items.Add(item); } PeerFinder_ConnectButton.Visibility = Visibility.Visible; PeerFinder_FoundPeersList.Visibility = Visibility.Visible; NotifyUser("Finding Peers Done", NotifyType.StatusMessage); }} catch { NotifyUser("No peers found", NotifyType.StatusMessage); PeerFinder_ConnectButton.Visibility = Visibility.Collapsed; PeerFinder_FoundPeersList.Visibility = Visibility.Collapsed; } } void PeerFinder_StartFindingPeers(object sender, RoutedEventArgs e) { NotifyUser("", NotifyType.ErrorMessage); if (!_peerFinderStarted) { // attach the callback handler (there can only be one PeerConnectProgress handler). PeerFinder.TriggeredConnectionStateChanged += new TypedEventHandler<object, TriggeredConnectionStateChangedEventArgs>(TriggeredConnectionStateChangedEventHandler); // attach the incoming connection request event handler PeerFinder.ConnectionRequested += new TypedEventHandler<object, ConnectionRequestedEventArgs>(PeerConnectionRequested); // start listening for proximate peers PeerFinder.Start(); _peerFinderStarted = true; if (_browseConnectSupported && _triggeredConnectSupported) { NotifyUser("Tap another device to connect to a peer or click Browse for Peers button.", NotifyType.StatusMessage); PeerFinder_BrowsePeersButton.Visibility = Visibility.Visible; } else if (_triggeredConnectSupported) { NotifyUser("Tap another device to connect to a peer.", NotifyType.StatusMessage); } else if (_browseConnectSupported) { NotifyUser("Click Browse for Peers button.", NotifyType.StatusMessage); } } } /// <summary> /// Invoked when this page is about to be displayed in a Frame. /// </summary> /// <param name="e">Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page.</param> protected override void OnNavigatedTo(NavigationEventArgs e) { if (_triggeredConnectSupported || _browseConnectSupported) { // Initially only the advertise button should be visible. PeerFinder_StartFindingPeersButton.Visibility = Visibility.Visible; PeerFinder_BrowsePeersButton.Visibility = Visibility.Collapsed; PeerFinder_ConnectButton.Visibility = Visibility.Collapsed; PeerFinder_FoundPeersList.Visibility = Visibility.Collapsed; PeerFinder_SendButton.Visibility = Visibility.Collapsed; PeerFinder_AcceptButton.Visibility = Visibility.Collapsed; PeerFinder_MessageBox.Visibility = Visibility.Collapsed; PeerFinder_MessageBox.Text = "Hello World"; if (IsLaunchedByTap()) { NotifyUser("Launched by tap", NotifyType.StatusMessage); PeerFinder_StartFindingPeers(null, null); } else { if (!_triggeredConnectSupported) { NotifyUser("Tap based discovery of peers not supported", NotifyType.ErrorMessage); } else if (!_browseConnectSupported) { NotifyUser("Browsing for peers not supported", NotifyType.ErrorMessage); } } } else { NotifyUser("Tap based discovery of peers not supported \nBrowsing for peers not supported", NotifyType.ErrorMessage); } } // Invoked when the main page navigates to a different scenario protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { if (_peerFinderStarted) { // detach the callback handler (there can only be one PeerConnectProgress handler). PeerFinder.TriggeredConnectionStateChanged -= new TypedEventHandler<object, TriggeredConnectionStateChangedEventArgs>(TriggeredConnectionStateChangedEventHandler); // detach the incoming connection request event handler PeerFinder.ConnectionRequested -= new TypedEventHandler<object, ConnectionRequestedEventArgs>(PeerConnectionRequested); PeerFinder.Stop(); if (_socket != null) { _socketClosed = true; _socket.Dispose(); _socket = null; } _peerFinderStarted = false; } } public bool IsLaunchedByTap() { bool isLaunchedByTap = _isLaunchedByTap; _isLaunchedByTap = false; return isLaunchedByTap; } public enum NotifyType { StatusMessage, ErrorMessage }; } } MainPage.xaml <phone:PhoneApplicationPage x:Class="PhoneApp1.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" 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 Text="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock Text="{Binding Path=LocalizedResources.PageTitle, Source={StaticResource LocalizedStrings}}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel Orientation="Vertical" Margin="0,10,0,0" Grid.Row="0"> <Button x:Name="PeerFinder_StartFindingPeersButton" Content="Advertise" Visibility="Collapsed" Margin="0,0,10,0"/> <Button x:Name="PeerFinder_BrowsePeersButton" Content="Browse for Peers" Visibility="Collapsed" Margin="0,0,10,0"/> <Button x:Name="PeerFinder_ConnectButton" Content="Connect To a Peer" Visibility="Collapsed" Margin="0,0,10,0"/> <ListBox x:Name="PeerFinder_FoundPeersList" Visibility="Collapsed"> </ListBox> </StackPanel> <StackPanel Orientation="Vertical" Margin="0,10,0,0" Grid.Row="1"> <Button x:Name="PeerFinder_AcceptButton" Content="Accept Connection Request" Visibility="Collapsed" Margin="0,0,10,0"/> <Button x:Name="PeerFinder_SendButton" Content="Send Message" Visibility="Collapsed" Margin="0,0,10,0"/> <TextBox x:Name="PeerFinder_MessageBox" Visibility="Collapsed" Width="400" Margin="0,0,10,0"/> </StackPanel> </Grid> <Grid x:Name="Output" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="1"> <TextBlock x:Name="PeerFinderOutputText" TextWrapping="Wrap" /> </Grid> <!--Uncomment to see an alignment grid to help ensure your controls are aligned on common boundaries. Remove or comment out before shipping your application.--> <!--<Image Margin="0" Source="/Assets/AlignmentGrid.png" Stretch="None" IsHitTestVisible="False" />--> </Grid> </phone:PhoneApplicationPage>
Конечно, вам нужно немного подправить файл Proprieties (я только что проверил все авторизации). И вот у вас есть простой способ отправки сообщений между смартфоном WP8. Я не мог попробовать это, потому что виртуальная машина не может получить доступ к Bluetooth, но я уверен, что когда я получу в свои руки реальные машины, это будет.
Также я должен упомянуть, что этот код является простым портом Proximity Lab для Windows RT, и можно только надеяться, что это позволит передавать данные с Windows Phone на Windows Tablet и наоборот.