[WCF .NET] Communicatie vanuit server naar een client

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik heb een WCF service geschreven in C#.NET, met daarnaast een Windows client en een website. De client maakt voornamelijk gebruik van de WCF service, terwijl de website op het moment enkel op dezelfde database (als de service) draait.

Ik heb nu een scenario waarbij ik communicatie vanuit de server naar een client wil hebben, maar ik zou niet weten hoe ik dat het best voor elkaar kan krijgen. In principe is communicatie met een WCF service altijd van client naar server. Ik heb ook gevonden dat je via dual binding / callbacks ook weer een berichtje terug kan sturen naar de client.

Echter wat ik nodig heb is ENKEL een bericht van server naar client.

Ik wil namelijk dat een knop op de website een actie in de client teweeg brengt. Oftewel:
1. Gebruiker klikt op knop website.
2. Website stuurt berichtje naar service dat gebruiker 1234 op de knop heeft geklikt.
3. Service stuurt berichtje naar client 1234 dat op de knop is geklikt.
4. De client van gebruiker 1234 doet iets.


Klopt het dat stap 3 in principe "onmogelijk" is? Hoe kan ik aan de kant van de service weten welke client ik moet aanroepen (en hoe roep ik die uberhaupt aan)? Aangezien het bericht vanuit de website komt kan dit volgens mij niet via dual binding / callbacks (toch?).


De enige optie die ik zie is dat de client continu zit te pollen naar de service "is er al op de knop gedrukt?". Dat gaat natuurlijk werken maar als het een beetje soepel moet werken (lees: de actie op de client komt toch wel binnen een paar seconden) dan moet de client dus heel vaak zitten pollen. Aangezien er veel clients tegelijk online kunnen zijn, waarvan er misschien 1 per uur op de knop klikt, lijkt me dit totaal niet efficient.

Is er geen andere optie?

Mijn iRacing profiel


Acties:
  • +2 Henk 'm!

  • itsjohan
  • Registratie: Januari 2001
  • Laatst online: 23:02
SignalR?

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik heb wel eens met SignalR gespeeld maar dat lijkt me een beetje overkill, niet? Ik zal er nog eens naar kijken.

Inmiddels heb ik door dat het misschien toch mogelijk moet zijn met een duplex / dual binding service. Ik moet clients laten 'registreren' tijdens een login (dat deed ik toch al) waarbij ik een instance van de callback interface in een dictionary opsla (met hun user id als key bijvoorbeeld). Die instance kan ik dan weer terug vinden als de website (ook een client van de service in principe) de service benaderd. Ik moet de service dan wel een single instance maken zodat alle users dezelfde instance gebruiken, ik weet niet of dat meteen helemaal goed gaat.

Ik krijg het echter nog niet voor elkaar aangezien ik een wsDualHttpBinding nodig heb, en momenteel een basicHttpBinding gebruik. Met wsDualHttpBinding lijkt een authentication / security noodzakelijk (ik kan niet vinden hoe ik die uit zet) en dat heb ik eigenlijk niet nodig. Ik krijg wat ik ook doe telkens errors die er op neer komen dat de service en client niet in hetzelfde domain zitten. Nee goh, de service draait op een webserver en de client gewoon lokaal... Kan dit niet??

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • marrik
  • Registratie: Augustus 2003
  • Laatst online: 20-07 13:35

marrik

Live long and prosper

Communicatie vanaf de server naar een client is altijd lastig. WCF is daar gewoon niet geschikt voor. Kijk maar eens naar wat blogs/video's van Udi Darhan. In een van zijn video's over reliable messaging doet hij een leuk voorstel: Gebruik gewoon een email. Alles is hier al voor ingericht en gestandariseerd. Vervolgens een exchange-achtige methode gebruiken voor het ontvangen van de email.

Polling is natuurlijk wel te doen mits je dit kunt doen zonder de processor belasting te verhogen. Maar zoals je al aangeeft is dit een 'dure' methode.

Mocht je applicatie draaien op Windows 8.x of hoger, dan zou je ook eens kunnen kijken naar een push bericht via Azure.

Marrik


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:17

Haan

dotnetter

NickThissen schreef op maandag 23 november 2015 @ 09:27:
Ik heb wel eens met SignalR gespeeld maar dat lijkt me een beetje overkill, niet? Ik zal er nog eens naar kijken.
Waarom overkill? Volgens mij is SignalR juist uitermate geschikt hiervoor.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

Ik ben niet bekend met WCF, of Microsoft technologiën in het algemeen, maar het eerste wat mij hier te binnen schiet is 'long-polling':

Wellicht heb je hier wat aan:
https://anthymecaillard.w...opment-with-long-polling/

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik heb toch nog eens naar SignalR gekeken, en het lijkt inderdaad gelukt te zijn. De hele WCF service komt er nu niet meer bij kijken, enkel een SignalR server gehost in de (MVC) website, en een SignalR client in de windows client.

Op de website heb ik een 'Hub' gemaakt die clients laat registreren met hun ID, en dit in een dictionary bij houdt in een controller (welke single instance is);
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    public class BroadcastHub : Hub
    {
        private BroadcastController _controller;

        public BroadcastHub()
        {
            _controller = BroadcastController.Instance;
        }

        public void BroadcastToClient(int userId, string message)
        {
            string connId = _controller.GetConnectionId(userId);
            if (connId != null)
            {
                Clients.Client(connId).BroadcastMessage(message);
            }
            else
            {
                UnregisterClient(userId);
            }
        }

        public void RegisterClient(int userId)
        {
            var connId = Context.ConnectionId;
            _controller.RegisterClient(userId, connId);
        }

        public void UnregisterClient(int userId)
        {
            _controller.UnregisterClient(userId);
        }
    }

    public class BroadcastController
    {
        private ConcurrentDictionary<int, string> _clients;

        private BroadcastController()
        {
            _clients = new ConcurrentDictionary<int, string>();
        }

        private static BroadcastController _instance;
        public static BroadcastController Instance
        {
            get { return _instance ?? (_instance = new BroadcastController()); }
        }

        public string GetConnectionId(int userId)
        {
            if (_clients.ContainsKey(userId)) return _clients[userId];
            return null;
        }

        public void RegisterClient(int userId, string connId)
        {
            if (_clients.ContainsKey(userId))
            {
                UnregisterClient(userId);
            }

            var result = _clients.TryAdd(userId, connId);
        }

        public void UnregisterClient(int userId)
        {
            string connId;
            _clients.TryRemove(userId, out connId);
        }
    }


Achter de knop op de website zit dan wat javascript om 'BroadcastToClient' aan te roepen:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
        $(function () {

            var id = @Model.Id;

            var hub = $.connection.broadcastHub;
            
            $.connection.hub.start().done(function() {
                $('#btnTest').click(function(e) {
                    hub.server.broadcastToClient(id, 'Test message');
                });
            });
        });


En ten slotte op de client kan ik naar het BroadcastMessage event luisteren:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        private async void InitSignalR()
        {
            var connection = new HubConnection("http://....signalr server adres...");
            var hub = connection.CreateHubProxy("BroadcastHub");
            await connection.Start();
            
            // Register
            hub.Invoke("RegisterClient", Service.User.Id);

            // Listen for message
            hub.On<string>("BroadcastMessage", OnHubMessageReceived);
        }

        private void OnHubMessageReceived(string message)
        {
            System.Windows.MessageBox.Show("Message received: " + message, "SignalR");
        }


Dit lijkt te werken maar is pas voor een client getest, ik weet niet hoe het zich gaat gedragen met meerdere clients. Ik weet dat SignalR terugvalt op Long Polling als er geen Websocket ondersteuning is, en ik weet ook vrij zeker dat er op Windows 7 geen websockets zijn (in Windows 10 volgens mij wel), dus achter de schermen zal voor veel gebruikers nog steeds long polling gebruikt worden. Ik heb vaker gelezen dat long polling problemen kan geven met veel gebruikers tegelijk, omdat er in principe voor elke gebruiker een thread bezig gehouden wordt (terwijl de verbinding actief gehouden wordt). Ik ben dus benieuwd hoe dit schaalt... maar dat is iets wat ik niet echt snel kan testen...

In ieder geval bedankt voor de tip, ik hoop dat het blijft werken.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

NickThissen schreef op dinsdag 24 november 2015 @ 17:00:
....
Ik heb vaker gelezen dat long polling problemen kan geven met veel gebruikers tegelijk, omdat er in principe voor elke gebruiker een thread bezig gehouden wordt (terwijl de verbinding actief gehouden wordt). Ik ben dus benieuwd hoe dit schaalt... maar dat is iets wat ik niet echt snel kan testen...
Bij traditionele webservers is schaalbaarheid met long polling inderdaad een probleem. Daarom gebruik ik voor het long-polling gedeelte een NodeJS server, deze heeft geen thread per connectie maar is event-driven, en is daardoor in dit geval beter schaalbaar.

Hier heb jij met .Net alleen weinig aan. Overigens is websocket support niet Windows versie afhankelijk maar browser afhankelijk.

[ Voor 10% gewijzigd door EddoH op 24-11-2015 19:46 ]


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
EddoH schreef op dinsdag 24 november 2015 @ 19:43:
[...]Overigens is websocket support niet Windows versie afhankelijk maar browser afhankelijk.
Mijn client is een windows applicatie, daar komt geen browser bij kijken dus? Misschien heb ik het mis maar ik heb toch ooit gelezen dat SignalR in een windows applicatie het communicatie type laat afhangen van windows zelf en dat er pas vanaf win10 websockets gebruikt wordt.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

NickThissen schreef op dinsdag 24 november 2015 @ 20:04:
[...]

Mijn client is een windows applicatie, daar komt geen browser bij kijken dus? Misschien heb ik het mis maar ik heb toch ooit gelezen dat SignalR in een windows applicatie het communicatie type laat afhangen van windows zelf en dat er pas vanaf win10 websockets gebruikt wordt.
Excuus, zoals gezegd ben ik niet bekend met WCF / .NET. Ik heb geen ervaring met websocket gebruik binnen een Windows applicatie. Een snelle google leerde mij echter dat SignalR client Websocket support afhankelijk is van Windows 8? http://stackoverflow.com/...561/signalr-w-web-sockets

Again, ik heb geen idee of dit precies het platform is wat jij gebruikt. Ik zal mij verder onthouden van commentaar bij gebrek aan specifieke .Net kennis ;)

Acties:
  • 0 Henk 'm!

  • FeronIT
  • Registratie: Mei 2015
  • Laatst online: 09-10 21:00
SignalR is een wrapper die zorgt dat de ontwikkelaar geen omkijken heeft naar de ondersteunde technieken. SignalR maakt, afhankelijk van de CLIENT gebruik van polling, long-polling of websockets. De server moet inderdaad IIS8 of hoge zijn, de client kan zelfs IE6 zijn.

Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

FeronIT schreef op dinsdag 24 november 2015 @ 20:50:
SignalR is een wrapper die zorgt dat de ontwikkelaar geen omkijken heeft naar de ondersteunde technieken. SignalR maakt, afhankelijk van de CLIENT gebruik van polling, long-polling of websockets. De server moet inderdaad IIS8 of hoge zijn, de client kan zelfs IE6 zijn.
Uh ja, dat zei ik toch? Alleen de TS gaf aan dat zijn client een Windows applicatie is. Blijkbaar heeft de SignalR client library die gebruikt worden door de applicatie een bepaalde versie van Windows nodig voordat hij intern Websockets kan gebruiken.

Acties:
  • 0 Henk 'm!

  • FeronIT
  • Registratie: Mei 2015
  • Laatst online: 09-10 21:00
Websockets is beschikbaar als de client over .net 4.5 beschikt:
http://www.asp.net/signal...hubs-api-guide-net-client , kijk bij het hoofdstuk: How to specify the transport method

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Hier staat dat Windows 8+ vereist is voor websockets:
http://www.asp.net/signal...arted/supported-platforms
(Onder Windows Desktop and Silverlight Applications)

Mijn iRacing profiel

Pagina: 1