読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

バックグラウンドでソケット通信

UWP

今ソケット通信がアツイ!!

というわけで、バックグラウンドでソケット通信をする方法です。

UWPのソケットのクラスであるStreamSocketクラスはバックグラウンドでソケット通信をする機能があったりします。

バックグラウンド通信の有効化

仮にRuntimeComponent1.SocketBackgroundTaskというバックグラウンドタスクがあるとして、ソケット通信をバックグラウンドに回すにか以下のようなコードになります。

private StreamSocket Socket { get; set; }

private IBackgroundTaskRegistration Task { get; set; }


private async void button_Click(object sender, RoutedEventArgs e)
{
    this.Task = BackgroundTaskRegistration.AllTasks
        .FirstOrDefault(x => x.Value.Name == "SocketTask")
        .Value;

    if (this.Task == null)
    {
        var builder = new BackgroundTaskBuilder();
        builder.Name = "SocketTask";
        builder.TaskEntryPoint = "RuntimeComponent1.SocketBackgroundTask";
        builder.IsNetworkRequested = true;
        builder.SetTrigger(new SocketActivityTrigger());
        this.Task = builder.Register();
    }

    var socketActivityInformation = default(SocketActivityInformation);
    if (!SocketActivityInformation.AllSockets.TryGetValue("MySocket", out socketActivityInformation))
    {
        this.Socket = new StreamSocket();
        await this.Socket.ConnectAsync(new HostName("127.0.0.1"), "9999");
        this.Socket.EnableTransferOwnership(this.Task.TaskId, SocketActivityConnectedStandbyAction.Wake);
        this.Socket.TransferOwnership("MySocket");
    }
}

そして、バックグラウンドタスクではSocketActivityTriggerDetailsを受け取って処理をします。

using ClassLibrary1;
using System.IO;
using Windows.ApplicationModel.Background;
using Windows.Networking.Sockets;

namespace RuntimeComponent1
{
    public sealed class SocketBackgroundTask : IBackgroundTask
    {
        public async void Run(IBackgroundTaskInstance taskInstance)
        {
            var d = taskInstance.GetDeferral();

            try
            {
                var detail = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
                switch(detail.Reason)
                {
                    case SocketActivityTriggerReason.SocketActivity:
                        using (var sr = new StreamReader(detail.SocketInformation.StreamSocket.InputStream.AsStreamForRead()))
                        {
                            var data = sr.ReadLine();
                            Class1.AddOnDispatcher(data);
                        }
                        detail.SocketInformation.StreamSocket.TransferOwnership("MySocket");
                        break;
                    case SocketActivityTriggerReason.KeepAliveTimerExpired:
                        detail.SocketInformation.StreamSocket.TransferOwnership("MySocket");
                        break;
                }
            }
            catch
            { }

            d.Complete();
        }
    }
}

Package.appxmanifestの宣言には、システムイベントとしてバックグラウンドタスクに登録します。エントリポイントにクラス名を指定するのを忘れずに。

これだけで、バックグラウンドでソケット通信ができます。

課題

これが出来たからといってアプリの方式どうしよう…Windowが出てるときはフォアグラウンドで通信しつつ最小化したらバックグラウンドに回してとか…バックグラウンドタスクとWindowの連携とかetc...