かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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

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

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

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...