Статьи

31 Дней Манго | День № 21: Розетки

Эта статья является Днем № 21 в серии под названием « 31 Дней Манго» и была написана приглашенным автором Параг Джоши . С Parag можно связаться через Twitter по адресу @ilovethexbox .

Сегодня мы познакомимся с новой поддержкой сокетов, анонсированной в Windows Phone 7.1. Итак, что такое сокеты? Проще говоря, это механизм связи, основанный на TCP / IP, который используется для удаленных приложений, таких как печать, видеоконференции и многое другое.

Прежде чем мы углубимся в то, как написать приложение для Windows Phone на основе сокетов, давайте разберемся, что такое сокеты.

Розетки — что это значит?

Приложения взаимодействуют через сокеты на основе адреса сокета. Адрес сокета представляет собой комбинацию адреса TCP / IP и порта. Используя эту информацию, приложения могут общаться друг с другом по локальной сети или через Интернет.

Статья, упомянутая ниже, дает очень хорошее объяснение терминов и технологий, стоящих за сокетами. Как программист, все, что вам нужно знать, это как подключиться (если используется TCP / IP) и как отправлять / получать данные.

Сокеты — технология низкого уровня. Это означает, что вам нужно определить данные, которые отправляются и как они читаются. Хотя это может привести к ошибкам разбора, оно дает вам огромное преимущество с точки зрения скорости передачи данных через веб-сервисы.

Вот ссылка на подробную статью о сокетах и ​​поддержке в Windows Phone 7.1: http://msdn.microsoft.com/en-us/library/hh202874(v=vs.92).aspx .

Что мы будем делать?

Мы собираемся создать простое приложение для приема заказов в ресторане. Будет приложение Windows Phone 7 для приема заказов, а также приложение Silverlight для браузера, которое будет принимать / обрабатывать заказы, которые будут работать на вашем компьютере или ноутбуке. Концепция заключается в том, что серверы будут принимать заказы на устройствах Windows Phone 7, а хост будет получать заказы и отправлять статусы заказов по мере их выполнения.

Серверы будут уведомлены о каждом заказе, когда он будет выполнен.

Это приложение основано на многоадресном приложении «Камень, ножницы, бумага» на MSDN ( http://msdn.microsoft.com/en-us/library/ff431744(v=vs.92).aspx ).

Итак, начнем!

Краткая информация

Как минимум, нам нужно создать серверное приложение, которое получает данные, отправленные клиентом. Для демонстрации мы выбрали приложение Silverlight (Out of Browser).

Примечание. Мы должны запустить приложение Silverlight в режиме «Out of Browser» с повышенными разрешениями, чтобы оно могло получить доступ к полученным данным сокета.

Нам нужно договориться о том, какие данные отправляются на хост -> номер таблицы, информацию о заказе и уровень специй.

/// <summary>
/// When we receive the Order, we pass on the table number and other parameters
/// </summary>

public class OrderReceivedEventArgs : EventArgs
{
    public int tableNumber { get; set; }
    public int spiceLevel { get; set; }
    public string order { get; set; }
}

Мы также отобразим устройства Windows Phone 7, подключенные к хосту. Это помогает человеку, управляющему хостом, знать, сколько серверов работает одновременно. Эта информация поступает из объекта «DeviceInfo». Вот фрагмент декларации:

/// <summary>
/// The information about each device. This class must implement INotifyPropertyChanged as it is
/// bound to a datagrid using an observable collection.
/// </summary>
public class DeviceInfo : INotifyPropertyChanged
{
    private IPEndPoint _DeviceEndPoint;
    private string _DeviceOrServerName;
    public DeviceInfo(string deviceOrServerName, IPEndPoint endPoint)
    {
        _DeviceEndPoint = endPoint;
        _DeviceOrServerName = deviceOrServerName;
    }
}

Вот скриншот хоста:

clip_image002

Нам также нужно, чтобы клиенты Windows Phone 7 принимали заказ — простое приложение, которое принимает вышеуказанные данные и отправляет их.

Вот скриншот приложения Windows Phone 7:

clip_image004

Как видите, у нас есть имя сервера вверху. В настоящее время это приложение создано, но в качестве расширения мы можем предоставить пользователю возможность войти в систему и использовать данные для входа, чтобы показать имя сервера, который вошел в систему.

У нас есть два слайдера:

1. Первый слайдер для выбора номера таблицы. Он варьируется от 1 до 10. Это очень упрощенный подход к выбору таблиц, который будет легко работать даже с 20-30 столами, поскольку ползунок очень точный и позволяет легко выбирать прикосновения.

2. Второй слайдер — выбор уровня специй. Это происходит главным образом из-за моего вкуса острой пищи, но это также очень часто указывается при заказе еды.

Диапазон устанавливается от 1 до 10.

Третий элемент — это текстовое поле для размещения деталей заказа. Если вы хотите расширить эту демонстрацию, вы можете добавить прокручиваемый выбор пунктов меню и сетку данных для добавления каждого выбранного пункта меню. Для этого примера мы сохранили его простым для ввода текста.

И, наконец, нам нужна кнопка для отправки заказа. Это оно!

Следует отметить следующие ключевые моменты:

1. Класс RestaurantCommands : этот класс содержит команды, разрешенные в нашем приложении сокетов, и должен быть общим для хоста и получателя, чтобы понимать сообщение, отправленное и полученное.

2. UdpAnySourceMulticastChannel.cs и UdpPacketReceivedEventArgs.cs : это два файла, которые мы взяли из приложения многоадресной рассылки sdk, которые содержат код сокетов многоадресной рассылки UDP для присоединения к группе, отправки и получения данных.

3. Класс заказа: этот класс обрабатывает все коммуникации приложений. Связь для приложения состоит из ряда команд, которые мы определили в классе RestaurantCommand.cs. Эти команды представляют собой грамматику или набор действий, которые мы можем передавать, получать и интерпретировать.

Понимание образца

Предварительные условия : Установите инструменты Mango с http://create.msdn.com . Это должно дать вам Visual Studio Express 2010 и Windows Phone SDK, которые необходимы для разработки приложений для Windows Phone. Мы также запускаем приложение Silverlight на хосте. Загрузите Visual Studio C # Express Edition, чтобы получить необходимые шаблоны.

  • Запустите Visual Studio, перейдите к файлу решения и откройте его.
  • Примечание. Файл решения содержит два запускаемых проекта. Проект Windows Phone 7 TakeMyOrder и приложение Silverlight PointOfSaleApp.
  • Это пример сокета многоадресной рассылки. Для этого вам необходимо развернуть образец Windows Phone 7 на телефоне и запустить приложение Silverlight на хосте.
  • Либо, если у вас нет телефона, разверните приложение Silverlight на другом компьютере (скажем, на виртуальной машине) и запустите приложение Windows Phone 7 в эмуляторе на хосте.
  • Вы не можете запустить приложение Silverlight и приложение Windows Phone 7 на эмуляторе на одном компьютере.
  • Если у вас есть несколько машин, телефонов, вы можете запустить приложение Windows Phone 7 на всех из них. Это даст вам представление о том, что несколько серверов принимают заказы и отправляют их одновременно.
  • Поскольку мы используем протокол UDP, нет никакой гарантии, что отправленные сообщения будут получены. Это связано с тем, что нет однорангового соединения между двумя устройствами. Вместо этого устройства присоединяются к многоадресной группе, идентифицируемой IP-адресом и портом. Однако, как вы увидите, когда вы запустите приложение, связь достаточно надежна.

Как это работает:

  • Запустите приложение Silverlight из браузера.
  • Запустите приложение Windows Phone 7 на телефоне или на эмуляторе, запущенном на другом компьютере.
  • Хост покажет имя сервера и адрес конечной точки.
  • На данный момент вы готовы сделать заказ.
  • В приложении Windows Phone 7, используйте ползунок таблицы и выберите таблицу от 1 до 10.
  • Введите заказ. Например, жареный рис с курицей и креветками. Колы нет льда и т. Д.
  • Используйте слайдер специй, чтобы установить желаемый уровень специй. 10 будет отличным выбором!
  • Нажмите Заказать. В этот момент приложение windows phone 7 отправляет команду «SendOrder» всем устройствам в группе. В качестве альтернативы мы могли бы отфильтровать это, чтобы просто отправить заказ на хост.
  • На хосте заказ получен и добавлен в сетку «Входящие заказы». Это отображается со статусом «В процессе» и присваивается уникальный идентификатор заказа (на основе количества полученных заказов).
  • Допустим, у нас очень быстрая кухня и заказ готов, как только мы его получим. Установите флажок рядом с заказом «В процессе» и нажмите кнопку «Готов к заказу». Хост отправляет статус заказа всем устройствам в сети и обновляет статус в сетке. Смотрите ниже относительно INotifyPropertyChanged.
  • Устройства Windows Phone 7 получают статус заказа на всех устройствах. В качестве альтернативы мы могли бы отфильтровать его, чтобы просто отправить на устройство, отправляющее заказ.

Вот скриншот после завершения заказа:

clip_image006

Понимание кода

Вот ключевые моменты, чтобы понять о коде.

Присоединение к группе: устройства присоединяются к группе, используя следующие ключевые компоненты:

/// <summary>
/// All communication takes place using a UdpAnySourceMulticastChannel.
/// A UdpAnySourceMulticastChannel is a wrapper we create around the UdpAnySourceMulticastClient.
/// </summary>
/// <value>The channel.</value>
private UdpAnySourceMulticastChannel Channel { get; set; }

/// <summary>
/// The IP address of the multicast group.
/// </summary>
/// <remarks>
/// A multicast group is defined by a multicast group address, which is an IP address
/// that must be in the range from 224.0.0.0 to 239.255.255.255. Multicast addresses in
/// the range from 224.0.0.0 to 224.0.0.255 inclusive are well-known reserved multicast
/// addresses. For example, 224.0.0.0 is the Base address, 224.0.0.1 is the multicast group
/// address that represents all systems on the same physical network, and 224.0.0.2 represents
/// all routers on the same physical network.The Internet Assigned Numbers Authority (IANA) is
/// responsible for this list of reserved addresses. For more information on the reserved
/// address assignments, please see the IANA website.
/// http://go.microsoft.com/fwlink/?LinkId=221630
/// </remarks>
private const string GROUP_ADDRESS = "224.0.1.11";

/// <summary>
/// This defines the port number through which all communication with the multicast group will take place.
/// </summary>
/// <remarks>
/// The value in this example is arbitrary and you are free to choose your own.
/// </remarks>
private const int GROUP_PORT = 54329;

Следующая команда выдается для присоединения к группе:

//Send command to join the multi cast group
App.Order.Join(serverName);

И, наконец, метод соединения открывает канал для общения.

/// <summary>
/// Join the multicast group.
/// </summary>
/// <param name="serverName">The server name I want to join as.</param>
/// <remarks>The server name is not needed for multicast communication. it is
/// used in this example to identify each member of the multicast group with
/// a friendly name. </remarks>
public void Join(string serverName)
{
    if (IsJoined)
       {
           return;
    }

       // Store my player name
       _serverName = serverName;

       //Open the connection
       this.Channel.Open();
}

Мы подписываемся на следующие события для обработки:

/// <summary>
/// Register for events on the multicast channel.
/// </summary>
private void RegisterEvents()
{
    // Register for events from the multicast channel
       this.Channel.Joined += new EventHandler(Channel_Joined);
       this.Channel.BeforeClose += new EventHandler(Channel_BeforeClose);
       this.Channel.PacketReceived += new EventHandler<UdpPacketReceivedEventArgs>(Channel_PacketReceived);
}

Особо следует отметить событие «PacketReceived». Мы обрабатываем все полученные команды (устройство и хост) в этом обработчике событий. Проще говоря, мы анализируем входящее сообщение и идентифицируем отправляемую команду. Основываясь на команде и количестве аргументов, мы дополнительно определяем действие, которое нужно предпринять.

Отправка заказа: Следующая команда выдается, когда пользователь нажимает кнопку «Заказ»:

App.Order.SendOrder(txtOrder.Text, TableSlider.Value.ToString(), spiceSlider.Value.ToString());

/// <summary>
/// Send the order
/// </summary>
/// <param name="Order"></param>
/// <param name="tableNumber"></param>
/// <param name="spiceLevel"></param>
public void SendOrder(string Order,string tableNumber, string spiceLevel)
{
    if (this.Channel != null)
       {
           //Send order to all devices. Only the server will process the send order command. Others will simply ignore it.
                this.Channel.Send(RestaurantCommands.SendOrderFormat, _serverName, Order, tableNumber, spiceLevel);
    }
}

Наш «SendOrderFormat» выглядит так:

public const string SendOrder = "SO";
public const string SendOrderFormat = SendOrder + CommandDelimeter + "{0}" + CommandDelimeter + "{1}" + CommandDelimeter + "{2}" + CommandDelimeter + "{3}";

Вот код для метода Send:

/// <summary>
/// Sends the specified format. This is a multicast message that is sent to all members of the multicast group.
/// </summary>
/// <param name="format">The format.</param>
/// <param name="args">The args.</param>
public void Send(string format, params object[] args)
{
    try
    {
        if (this.IsJoined)
        {
            byte[] data = Encoding.UTF8.GetBytes(string.Format(format, args));
            this.Client.BeginSendToGroup(data, 0, data.Length, new AsyncCallback(SendToGroupCallback), null);
        }
    }
    catch (SocketException socketEx)
    {

        // See if we can do something when a SocketException occurs.
        HandleSocketException(socketEx);
    }
    catch (InvalidOperationException)
    {
        Debug.WriteLine("BeginSendToGroup IOE");
    }
}

Итак, как мы видим, в зависимости от формата сообщение форматируется и передается.

Получение заказа:

На хосте сообщение получено обработчиком, определенным для события «PacketReceived»: void Channel_PacketReceived (отправитель объекта, UdpPacketReceivedEventArgs e)

Разбираем сообщение и определяем команду.

string message = e.Message.Trim('\0');
string[] messageParts = message.Split(RestaurantCommands.CommandDelimeter.ToCharArray());

else if (messageParts.Length == 5 && messageParts[0]== RestaurantCommands.SendOrder)
{
    //Status of order received
       OrderReceivedEventArgs args = new OrderReceivedEventArgs();
       args.tableNumber = Convert.ToInt32(messageParts[3]);
       args.spiceLevel = Convert.ToInt32( messageParts[4]);
       args.order = messageParts[2];
       if (DataReceivedFromDevice != null)
       {
           DataReceivedFromDevice(this, args);
    }
}

Это очень простой механизм общения. Как только мы определили, проанализировали и создали наши аргументы для метода полученных данных, мы его поднимаем.

В нашем хост-приложении мы обрабатываем событие и добавляем входящий заказ в коллекцию заказов:

/// <summary>
/// Handles incoming orders
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Order_DataReceivedFromDevice(object sender, OrderReceivedEventArgs e)
{
    int OrderID = App.OrdersCollection.Count +1;
       Orders objOrders = new Orders(OrderID, e.tableNumber, e.spiceLevel, e.order);
       App.OrdersCollection.Add(objOrders);
}

Известные вопросы

· Время от времени вы получаете сообщение об ошибке, когда прекращаете отладку:

· Имя файла или сборки «System.Net.debug.resources, версия = 2.0.5.0, Culture = en-US, PublicKeyToken = 7cec85d7bea7798e» или одна из его зависимостей не найдена.

· Это известная проблема, указанная здесь http://forums.create.msdn.com/forums/p/89666/537141.aspx . Иногда после появления этой ошибки вам необходимо закрыть Visual Studio и перезапустить ее.

INotifyPropertyChanged

Этот интерфейс должен быть реализован на объектах, которые мы используем для привязки к сетям данных. Даже если коллекции объектов объявлены, как показано ниже, пользовательский интерфейс не обновляется, если только свойства в самом объекте не вызовут событие изменения свойства.

public static ObservableCollection <DeviceInfo> Devices = new ObservableCollection <DeviceInfo> ();

Статус заказа на отправку : аналогично вышеописанному, аналогичный механизм реализован для отправки статуса заказа:

/// <summary>
/// Sends the order status
/// </summary>
/// <param name="OrderID"></param>
/// <param name="status"></param>
public void SendOrderStatus(int OrderID, string status)
{
    if (this.Channel != null)
{
           //Send order to all devices. Only the server will process the send order command. Others will simply ignore it.
this.Channel.Send(RestaurantCommands.ReceiveOrderFormat, _serverName, OrderID, status);
}
}

public const string ReceiveOrder = "RO";
        
public const string ReceiveOrderFormat = ReceiveOrder + CommandDelimeter + "{0}" + CommandDelimeter + "{1}" + CommandDelimeter + "{2}";

else if (messageParts.Length == 4 && messageParts[0] == RestaurantCommands.ReceiveOrder)
{
    //Status of order received
       OrderStatusReceivedEventArgs args = new OrderStatusReceivedEventArgs();
       args.orderId = Convert.ToInt32(messageParts[2]);
       args.orderStatus = messageParts[3];
       if (DataReceivedFromDevice != null)
       {
        DataReceivedFromDevice(this, args);
    }
}

/// <summary>
/// Shows a message box with the order status
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Order_DataReceivedFromDevice(object sender, OrderStatusReceivedEventArgs e)
{
    DiagnosticsHelper.SafeShow("Order Status of '" +e.orderStatus + "' Received For Order:" + e.orderId.ToString());
}

Резюме

Таким образом, с помощью предоставленных помощников довольно просто реализовать приложение с использованием сокетов. Это приложение можно превратить в комплексное ресторанное приложение, просто расширив некоторые функции, как отмечалось, и многие другие:

  • Добавить возможность выбора из меню
  • Добавьте возможность видеть отложенные ордера на устройствах
  • Добавить возможность входа

Основные шаги по созданию приложения сокета:

  • Определите тип приложения сокета: Multicast (UDP) или UniCast (TCP).
  • Определитесь с командами.
  • Связь.
  • Процесс.

Это должно дать вам хороший обзор поддержки сокетов в Windows Phone 7. Ознакомьтесь с примерами кода, предоставленными по адресу http://msdn.microsoft.com/en-us/library/hh202874(v=vs.92).aspx

Чтобы загрузить весь пример приложения Windows Phone с помощью сокетов, нажмите кнопку «Загрузить код» ниже:

скачать

Завтра Мэтт Эланд проведет вас через App Connect (также известный как расширяемость поиска), чтобы вы могли увидеть ваше приложение перед пользователями, даже если они не установили его на своем устройстве. Увидимся позже!

toolsbutton