Share via


UWP + Linux Socket communication & Handling communciation in the BackgroundTask

Introduction

This article we are going to learn, how to handling the socket communications in the BackgroundTask  .  

Quick Overview of Socket Communications in the BackgroundTask.

Why has it required?

When the GUI leaving in the foreground due to some synchro like app has suspended in the foreground, delegate this job to the background task, background Task will be handling the communication.

Socket communications in the BackgroundTask will handle into the two ways as given below, those triggers that launch the background task.

1.     SocketActivityTrigger

       2.   ControlChannelTrigger

Creating the StreamSocket (TCP), DatagramSocket(UDP) protocols use the SocketActivityTrigger  , WebSocket-related protocol like HttpClient we must use the ControlChannelTrigger.

Transfer the Socket Communications

Transfer the StreamSocket to the BackgroundTask, first, we have to Enable the transfer the ownership, to enable the use the EnableTransferOwnership API. To call this function in Server Side socket before the binding host address, In Client side call this function, before Connect to the server socket. 

Transfer the socket:  Based on the needs, call the TransferOwnerShip API to transfer the socket communication to the Background Task.(Socket Broker) , In this example OnSuspending event, we call the TransferOwnership API.

Concept of this sample

Using TCP protocol, Server sends the continues data to the client, the client will receive the data’s display in GUI, Once Client App has suspended, BackgroundTask of the app receives the data’s display as a ToastNotification.

Prerequisites

The server application is running in the Linux PC, the Client application is running on the Windows 10 PC.

Client

1.     Windows 10

2.     Visual studio 2017 RC With UWP tools

3.     C#

4.     UWP Toolkit

Server (Any Server)

1.     Linux mint 18

2.     Visual Studio Code IDE

3.     C Programming

4.     Oracle Virtual Box

Server Socket Programming  


​Server socket programming run in the Linux mint 18 and programming has written in the "C" language using Visual studio Code editor.
Linux OS is running in the Oracle virtual box  ( Both server and client is running same windows 10 pc).

01.#include <unistd.h>
02.#include <stdio.h>
03.#include <stdlib.h>
04.#include <netdb.h>
05.#include <netinet/in.h>
06.#include <string.h>
07. 
08.int main()
09.{
10.    printf("UWP Socket Demo\n");
11. 
12.     int sockfd, newsockfd, portno, clilen;
13.   char buf[256];
14.   struct sockaddr_in serv_addr, cli_addr;
15.   int  n;
16.     
17.    sockfd = socket(AF_INET, SOCK_STREAM, 0);
18.    
19.   if (sockfd < 0)
20.    {
21.      perror("socket created error");
22.      exit(1);
23.   }
24. 
25.   bzero((char *) &serv_addr, sizeof(serv_addr));
26.   portno = 6000;
27.    
28.   serv_addr.sin_family = AF_INET;
29.   serv_addr.sin_addr.s_addr = INADDR_ANY;
30.   serv_addr.sin_port = htons(portno);
31.    
32.    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
33.      perror("ERROR on binding");
34.      exit(1);
35.   }
36. 
37.    printf("socket created\n");
38.     
39.    
40.   listen(sockfd,5);
41.   clilen = sizeof(cli_addr);
42.    
43.   printf("listen has accepted\n");
44. 
45.     
46.   newsockfd = accept(sockfd,(struct sockaddr *)&cli_addr, &clilen);
47.     
48.   if (newsockfd < 0) {
49.      perror("ERROR on accept");
50.      exit(1);
51.   }
52.    
53.   printf("Welcome new client\n");
54.    
55.   printf("Here is the message: %s\n",buf);
56.    bzero(buf,256);
57.        n = read(newsockfd,buf,255);
58.         
59.        if (n < 0) {
60.            perror("ERROR reading from socket");
61.            exit(1);
62.        }
63.    
64.   int i = 0;
65.    
66.   while(1)
67.   {
68.     
69.      char str[20];
70.      int len = sprintf(str,"value : %d",i++);
71.      n = write(newsockfd,str,len);
72.       
73.      if (n < 0) {
74.          perror("Send Failed!!!\n");
75.          exit(1);
76.      }
77.      sleep(10);
78. 
79.      printf("Send %d \n",i);
80.   }
81.    return 0;
82.}

compile & run the code

YouTube link to view the Server Running 

View

Create a BackgroundTask Component

1.     File -> New Project -> Templates -> Visual C# -> Windows Universal -> Windows Runtime Component (Universal Windows)

2. Convert the TriggerDetails as SocketActivityTriggerDetails and get socket information, 
this example handling only the SocketActivity event only 

01.public async void Run(IBackgroundTaskInstance taskInstance)
02.        {
03.            _bgDeferral = taskInstance.GetDeferral();
04. 
05.      var socketActivate = taskInstance.TriggerDetails as SocketActivityTriggerDetails;
06.            if (socketActivate != null)
07.            {
08.                var socketInfo = socketActivate.SocketInformation;
09. 
10.                StreamSocket socket;
11. 
12.                switch (socketActivate.Reason)
13.                {
14.                    case SocketActivityTriggerReason.None:
15.                        break;
16.                    case SocketActivityTriggerReason.SocketActivity:
17.                        socket = socketInfo.StreamSocket;
18.                        await ReadRecevice(socket);
19.                        socket.TransferOwnership(socketActivate.SocketInformation.Id);
20.                        break;
21.                    case SocketActivityTriggerReason.ConnectionAccepted:
22.                        break;
23.                    case SocketActivityTriggerReason.KeepAliveTimerExpired:
24.                        break;
25.                    case SocketActivityTriggerReason.SocketClosed:
26.                        break;
27.                    default:
28.                        throw new  ArgumentOutOfRangeException();
29.                }
30.            }
31. 
32.            _bgDeferral.Complete();
33. 
34.        }

 

3.Read the packet information & update the Trigger the Toast Notifications

01.private static  async Task ReadRecevice(StreamSocket socket)
02.        {
03.            var reader = new  DataReader(socket.InputStream)
04.            {
05.                InputStreamOptions = InputStreamOptions.Partial
06.            };
07. 
08.            await reader.LoadAsync(256);
09. 
10.            var response = reader.ReadString(reader.UnconsumedBufferLength);
11.            reader.DetachStream();
12. 
13.            var tost = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText01);
14.            var childUpdate = tost?.GetElementsByTagName("text");
15.            if (childUpdate != null) childUpdate[0].InnerText = response;
16.            ToastNotification titleNotification = new ToastNotification(tost) {Group = "NetUpdate"};
17.            ToastNotificationManager.CreateToastNotifier().Show(titleNotification);
18.        }

 UWP Client Application

 1. Create a client application
File -> New Project -> Templates -> Visual C# -> Windows Universal -> Blank App

 

  1. Adding Background component into UWP client application

Goto Package.appxmanifest file -> Select Declarations tab ->click Add & add the Background Tasks -> Select System event
Entry point , enter the BGComponent , namespace with classname ( Ex : BGSocket.BgSocket)

  1. Enable the Internet client options in Capacities from Package.appxmanifest file

2. Register the BackgroundTask which has created in the BackgroundTask Component, section
    Here we are using the UWP Toolkit helper class to register the BackgroundTask.

1.private void  CreateBgTask()
2.       {
3.           IBackgroundTrigger socketTrigger = new  SocketActivityTrigger();
4._bgSocketask = BackgroundTaskHelper.Register(BgTaskName, BgEntryPoint, socketTrigger);
5.       }

sequential diagram

overall client application implementation has described in the sequential diagram , each steps has explained below. 

1.     Create a Stream socket object after that call the EnableTransferOwnerShip, to enabletransferOwnership BackgroundTask Id has required and one more property is available and this is optional SocketActivityConnectedStandbyAction, this is property will use to decide socket broker (background Task) should receive the packet in system standby mode or not.

01.private async void CreateSocket()
02.        {
03.            try
04.            {
05.                HostName hostName = new  HostName("192.168.2.116");
06.                _clientSocket = new  StreamSocket();
07. 
08.                _clientSocket.Control.NoDelay = false;
09. 
10.                if (_bgSocketask != null)
11.                {
12.                    //EnableTransferOwnership
13.                    _clientSocket.EnableTransferOwnership(_bgSocketask.TaskId, SocketActivityConnectedStandbyAction.Wake);
14.                }
15. 
16.                //Connect the Server socket
17.                await _clientSocket.ConnectAsync(hostName, "6000");
18. 
19.                Stream streamOut = _clientSocket.OutputStream.AsStreamForWrite();
20.                StreamWriter writer = new  StreamWriter(streamOut);
21.                string request = "test";
22.                await writer.WriteLineAsync(request);
23.                await writer.FlushAsync();
24. 
25.                while (send)
26.                {
27.                    var reader = new  DataReader(_clientSocket.InputStream)
28.                    {
29.                        InputStreamOptions = InputStreamOptions.Partial
30.                    };
31.                    //Read the packet information
32.                    await reader.LoadAsync(256);
33.                    var response = reader.ReadString(reader.UnconsumedBufferLength);
34.                    reader.DetachStream();
35. 
36.                    //Display in GUI
37.                    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
38.                    {
39.                        dataInfo.Add(response);
40.                    });
41.                }
42.            }
43.            catch (Exception)
44.            {
45. 
46.            }
47.        }

2.  TransferOwnership API is use to transfer the call to the Socket Broker(BackgroundTask) & this function should be call in the On-Suspending Event in App.xaml.cs file.

01.public async void TransferCall()
02.        {
03.            if (_clientSocket == null) return;
04.            await _clientSocket.CancelIOAsync();
05. 
06.            var contextWrite = new  DataWriter();
07.            contextWrite.WriteInt32(_count);
08.            var socketContext = new  SocketActivityContext(contextWrite.DetachBuffer());
09.            _clientSocket.TransferOwnership(socketId, socketContext);
10.        }

 App.xaml.cs file

1.private void  OnSuspending(object  sender, SuspendingEventArgs e)
2.        {
3.            var deferral = e.SuspendingOperation.GetDeferral();
4.            mainPage.TransferCall();
5.            deferral.Complete();
6.        }

Sample Xaml Code

01.<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
02.        <Grid.RowDefinitions>
03.            <RowDefinition Height="Auto"/>
04.        </Grid.RowDefinitions>
05. 
06.        <StackPanel Margin="10,20,0,0"  Grid.Row="0" Orientation="Horizontal">
07.            <ScrollViewer>
08.                <ListBox x:Name="lstBox"  ScrollViewer.VerticalScrollBarVisibility="Auto"/>
09.            </ScrollViewer>
10.        </StackPanel>
11.    </Grid>

Run Server + Client application

youtube  Output video:

View

Download Source code

Conclusion 

Hope you understand , how BackgroundTask handling the socket communication. 
Please share your feedback & Suggestions

Thanks & Regards,
Vinoth.