Behöver råd kring client/server-modell

Diskussion i 'Frågor, support och diskussion' startad av uppdandom, 18 okt 2011.

  1. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Hej!

    Jag är nybliven Android-programmerare med en hel del erfarenhet från javaprogrammering. Jag har påbörjat programmering av en app som är ett turn-based brädspel med en spelplan där spelare ska kunna spela mot varandra.

    Klienten är så gott som färdigprogrammerad, en skiss till hur kommunikationen mellan klient och server ska gå till är under utveckling, och jag vill nu börja betatesta lite genom att ha både en server och en klient igång.

    Problemet: Jag kan nästan ingenting om servrar och behöver lite hjälp med att bli pekad i rätt riktning så att jag kan påbörja serverprogrammeringen. Jag är trött på ändlösa timmars googlande som bara gör mig mer förvirrad över vilken teknik jag bör använda.

    Vad är jag ute efter?
    1) En server som körs på något webhotell (inte som dedikerad dock), det vill säga jag vill enkelt kunna komma igång med programmeringen av servern utan att behöva sätta upp en egen server (det har jag inte datorkraft för).
    2) En server där jag helst kan använda java för programmeringen
    3) En server som smärtfritt hanterar ett större antal klienter.
    4) För spelet så kommer kommunikationen mellan server och klient att ske genom http requests (med datan paketerad i JSON/XML). På Androidsidan har jag i nuläget en primitiv implementering av Apaches HTTP-klient.

    Servern ska kunna kommunicera med spelarna via http requests och även pusha ut data via C2DM. På servern ska det lagras information om spelets tillstånd och status (såsom spelplanens aktuella utseende, hur mycket tillgångar varje spelare har m m). Som javaprogrammerare tänker jag mig klasser såsom Game, Board, User etc på servern.

    Statistik och användarinfo ska sparas i en mySQL-databas som kommunicerar med servern. När en spelare gör ett drag (genom att helt enkelt klicka i appen) så ska det skickas info om draget till servern som uppdaterar tillståndet och sedan ska skicka ut ny info till båda spelarna (till den som skickade in draget via vanlig response och till den andre via C2DM för att få till en push).

    Jag har dock ingen aning om vad som är bästa sättet att implementera detta om jag vill att punkt 1) - 4) ska vara uppfyllda.

    Tacksam för alla synpunkter och idéer och knuffar i rätt riktning!
     
  2. zappatilias

    zappatilias Infant Droid Medlem

    Blev medlem:
    9 okt 2009
    Inlägg:
    5
    Mottagna gillanden:
    0

    MINA ENHETER

    Jag hade nog rekommenderat Google App Engine (GAE) för ditt behov.

    GAE är väldigt enkelt att sätta upp och stödjer Java (även python och Go). Gratis är det med (vissa saker kostar, men tror gratisvarianten räcker gott och väl). http://appengine.google.com
     
  3. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack för svaret!

    Har funderat lite på GAE. Två frågor:

    1) Kan jag köra .java- eller .jar-filer på GAE, eller är det servlets och JSP som gäller på Javasidan?

    2) Om jag skulle använda servlets, hur ser jag till att min servlet håller koll på alla klienter, det vill säga om jag ska skicka ut data till låt säga tre specifika klienter, hur vet servleten vart den ska skicka http-requests? Utnyttjar den sessions-id?

    2) Finns det möjlighet att göra en mySQL-koppling I GAE?
     
  4. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Eftersom du föredrar Java så är det väl inte så dumt att köra med GAE som backend. Finns t.o.m. en färdig demoapp för C2DM som körs på GAE: JumpNote.

    Notera att du inte kan ansluta rakt av till MySQL från GAE, du får använda deras egna datastore. Din app får nämligen inte göra anslutningar ut från deras JVM sandbox annat än HTTP/S.

    Du kan naturligtvis sätta upp ännu en webserver som agerar webservice/API till din MySQL-databas. För den här typen av applikation verkar det dock j-vligt bökigt, bättre att antingen bygga hela backenden på en plattform som snackar direkt med MySQL alternativt använda GAE's datastore.

    Själv skulle jag byggt backenden i PHP, men det handlar om personliga preferenser och kunskaper. För tydlighetens skull kan jag ju bara nämna att den typen av "server" du är ute efter är i princip vilket webbhotell som helst - om vi bortser från att språket måste vara Java ;)
     
  5. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Servleten kommer väl sannolikt aldrig behöva skicka http requests till klienterna? Det är ju klienterna som kommer anropa servleten som då svarar?

    I så fall behöver inte servleten hålla kollen på särskilt mycket alls annat än att verifiera att klienten har rätt att göra anropet, sannolikt via nån form av autenticeringsdata såsom sessionid eller liknande.

    Kan berätta om mitt senaste projekt (RummiFight) som fungerar på ett liknande sätt som du är ute efter: Att kunna ha backend på ett vanligt webbhotell. Jag har löst det genom att sätta upp en databas på hotellet och lägga en knippe scripts på webservern (i mitt fall skrivna i C#.NET) som klistrar ihop de mobila enheterna med databasen.

    Så det är möjligt. Vill bara "varna" för att prestandan på webhotell tyvärr kan sämre än man tror. Jämför jag med min dedicerade server som står hemma så är den en faktor 500 snabbare än webhotellet... och detta trots att det "bara" är en 2GHz-maskin, inget monster.

    Sen finns ju alltid risken att nån annan användare på webhotellet plötsligt skriver ett uselt script som äter upp alla resurser eller att den peakar vid hög belastning. Men visst, det är fullt möjligt att jäkligt prisvärt om man får till det.

    För att "övervaka" spelet eller styra det så är det ju fullt möjligt att du snickrar ihop ett program som körs på datorn hemma och som anropar din databas på webhotellet för att fixa saker. Det finns ju inget som krävs att den ska köras på webhotellets maskin.
     
    Last edited: 19 okt 2011
  6. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack för svaret uncoloured! Och du har ju helt rätt. Det jag behöver är väsentligen vad varje webbhotell klarar av - om jag inte envisas med Java.

    Hade faktiskt av någon anledning eliminerat PHP som möjlighet, men det känns nu på nåt sätt ett uppenbart alternativ som jag borde tänkt på tidigare med tanke på att jag har erfarenhet av PHP och mySQL och att PHP nu är objektorienterat - vilket borde fungera bra med det jag vill göra.

    Min erfarenhet är dock från hemsideskapande, så jag behöver lite guidning. Om jag tänker rätt nu, så kommer alltså ett php-script på lämpligt webhotell att fungera som min "server". Scriptet tar emot data från klienter som ansluter, uppdaterar medlemsvariabler i spelklasser som jag instantierat och kommunicerar med en mySQL för att hämta/lagra data.

    Några frågor:
    1) Om jag vill använda PHP som en multiclient backend, är det bästa sättet att utnyttja sockets och en while(true)-loop som konstant lyssnar efter anslutningar? Typ så här någonting: PHP: Socket Functions - Manual

    2) Om jag bara vill skicka diverse JSON-data mellan klienten och servern, är det bättre med ren socket-kommunikation än att skicka http-requests?

    3) Om jag vill skicka ut data till en/flera klienter kan jag då kommunicera med dessa via de sockets jag får in under 1)? Jag vill till exempel kunna välja vilka klienter jag vill skicka en viss data till.

    4) Finns det några potentiella sandbox-problem med att från scriptet posta httprequests till googles C2DM-server?
     
    Last edited: 19 okt 2011
  7. foobar17

    foobar17 Kid Droid Medlem

    Blev medlem:
    12 jun 2009
    Inlägg:
    56
    Mottagna gillanden:
    11

    MINA ENHETER

    Om du kan tänka dig att host:a webapp:en på ett webbhotell, så kan jag varmt rekommendera webbramverket Grails, som använder språket Groovy.

    Groovy är typ Java++ och därför mycket enkelt att börja med för en Java programmerare. Grails använder i bakom kulisserna Spring MVC, Hibernate och SiteMesh, men man programmerar i Groovy. En av styrkorna är iden om configuration-by-convention, vilket innebär ett minimum av konfigurering, för att sätta upp en MySQl koppling t.ex.

    Läs mer på Grails - The search is over. och Groovy - Home
     
  8. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack ReaPadda! Jag hann posta innan jag såg ditt svar.

    Det verkar fortfarande vara något som jag inte förstår riktigt. Ja, det är ju klienterna som kommer anropa servleten som svarar. Jag vill dock att om till exempel tre spelare spelar samtidigt och en av spelarna gör ett drag, så kommer spelaren att skicka data om draget till servern. Servern behöver kunna informera samtliga spelare om detta drag. Hur håller servern koll på vart den ska skicka denna info till just dessa tre spelare?

    Jag tänker mig två scenarion där det senare är det jag helst skulle vilja ha:

    1) Klienterna anropar med jämna mellanrum servern för att kolla om det finns någon ny data att hämta. Om det finns det så svarar servern med denna data. Hur vet servern att den som anropar är en som ska ha datan - d v s är en spelare för just den spelplan som det dykt upp ny data från?

    2) Servern pushar ut info till berörda spelare om att det finns ny data att hämta. Dessa anropar då servern som levererar den nya datan.

    I båda fallen är dock det jag fortfarande inte förstår hur jag kan få min servlet att skicka ut data till vissa bestämda klienter, beroende på om de är delaktiga i ett visst spel eller inte. Tacksam för hjälp!
     
  9. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack för tipset! Vill i första hand försöka följa PHP-spåret, men kommer att ta en titt på Grails eftersom det verkar väldigt intressant för en gammal Java-programmerare. Är Grails nåt som de flesta webbhotell stödjer?
     
  10. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Kan du utveckla hur "klistrandet" går till? I mitt fall tänker jag mig PHP-script som får sköta det hela. Utnyttjar du sockets eller hur vet dina scripts vart de ska skicka data?
     
  11. dhanjel

    dhanjel Senior Droid Medlem

    Blev medlem:
    9 dec 2009
    Inlägg:
    1 414
    Mottagna gillanden:
    278
    Operatör:
    Telia
    Telefon:
    iPhone 11

    MINA ENHETER

    Operatör:
    Telia
    Telefon:
    iPhone 11
    Jag brukar köra PHP med nusoap som webservice-ramverk, och json som informationsstruktur.
     
  12. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Varje spelare har ett unikt ID som skickas med i varje request och din databas håller kollen på vilka PlayerID's som hör till vilken spelplan.

    Jag har löst det som så i mitt spel att jag använder en kombination. Jag skickar ut små socket-meddelanden till berörda klienter att "nu är det dags för dig att hämta data". Därefter gör de en http-request till avsett script som skickar tillbaka informationen i ett format som är läsbart för klienten.

    Problemet med sockets är bara att själva connectionen MÅSTE ske ifrån klienten till servern. Du kan av brandväggsskäl mycket sällan öppna en socket ifrån servern till klienten. Därför måste kopplingen redan vara gjord när du ska skicka meddelandet.

    Om du inte kommer på nåt sätt att lösa det så får du väl ha nån form av polling. Kanske ett jättelitet och optimerat script som anropas med regelbundna intervaller och bara returnerar att "japp, nu är det dags att hämta data" varefter klienten gör anropet till den "större" funktionen som kanske är lite tyngre för servern.

    Sen FINNS det ju en variant till, som sagt, och det är att använda sig av C2DM för att notifiera klienten om att det är dags att hämta data. Nackdelen med detta är att klienten måste köra Android 2.2 eller högre för att det ska funka. Är detta ok med dig så är det mycket enklare än att hålla på med socketstrafik.
     
  13. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Hjärtat i min server är en databas. Med andra ord så hämtar och lämnar mina scripts data ifrån denna. Att koppla ett webscript mot en databas är ju enkelt och kan göras i alla scriptspråk.

    Varje request från klienten till ett script innehåller data som identifierar vilken spelare som gör requesten och om den är "inloggad" osv. Därefter returnerar SAMMA script data tillbaka omedelbums, vilket då läses av klienten som därefter gör vad den ska med detta.

    Jag kör för övrigt synkrona http-anrop från min klient vilket innebär att den väntar tills den fått svar. En nackdel med detta är att man alltid måste lägga såna på en bakgrundstråd för att inte få upp errormeddelande om att appen låst sig om requesten tar tid.
     
  14. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack så mycket för informationen och hjälpen! Det börjar klarna så smått och jag ser en väg vidare. Kodningen i sig har aldrig varit ett problem, men implementeringen av serverdelen har gjort mig j-igt frustrerad ett par dagar nu och bromsat upp mitt projekt rejält.

    Låt mig bara se om jag förstått det hela rätt. Så här tänker jag mig att det hela fungerar i mitt fall:

    0) Användarna kör en apache http-klient i en separat tråd i klienten. Servern kör PHP-script och mySQL. Servern ligger i en lyssnarloop och väntar på nya anslutningar/ny data.

    1) En användare loggar in med användarnamn och lösenord. Ett PHP-script hanterar inloggningen genom att kolla mot registrerade användare i databasen. Om inloggningen lyckas skickas ett svar till klienten om att inloggningen lyckats och svaret innehåller även användarens unika ID (som tilldelats och finns lagrat i databasen sedan tidigare) som lagras hos klienten.

    2) På klientsidan väljer spelaren att starta en ny spelomgång. Information om detta önskemål (tillsammans med spelarens ID) skickas till ett PHP-script som försöker matcha spelaren mot andra som skickat in ett önskemål om att spela. När scriptet hittat en sådan skapas en ny spelomgång med ett visst game_ID och sen ska info skickas ut till alla spelare om att en spelomgång ska starta.

    Det är här jag fortfarande är lite osäker på hur scriptet håller koll på vart den ska skicka datan. Det krävs väl sockets här? Det vill säga för varje ny användare som ansluter till servern lagras dess socket i någon datastruktur. Till exempel en ArrayList<"user id","socket">. När scriptet ska skicka ut data till alla spelare i en spelomgång hämtar det sockets för alla inblandade ID:n, loopar igenom dessa sockets och skickar data till alla som ska ha data.

    Tänker jag fel här? Om inte så kräver väl detta att samtliga spelare håller sina sockets öppna och vid liv hela tiden? Eller pollar klienterna med jämna mellanrum med http-requests, scriptet kollar om det finns ny data för deras ID och scriptet svarar med en http-response i samma session?

    3) När spelet väl startats kommer det i princip att fungera på samma sätt hela tiden. En spelare gör något på spelbrädet. Info om detta skickas till servern, där ett script tar hand om informationen, uppdaterat spelets tillstånd etc. Nu ska alla spelarna återigen informeras om att det skett en förändring (så att spelplanen kan uppdateras m m). Det är ju enkelt att svara spelaren som skickade in datan via en http-response. De andra spelarnas id lokaliseras via game_id (som alltid skickas med).

    Nu är dock frågan densamma som ovan: Hur kommer infon om det förändrade speltillståndet till spelarna som inte agerade? Antingen måste det ju ske via öppna sockets som finns lagrade sedan tidigare, eller så lagras info om att det finns ny data för ett visst ID och när spelaren med detta ID så småningom pollar servern så svarar servern med denna information.

    Tacksam för synpunkter på om jag tänker rätt eller om jag missat någon väsentlig detalj i det hela?

    Tänker jag rätt för övrigt när jag tolkar

    som att det bibehålls en öppen socket mellan klient och server hela tiden? Med tanke på att som du skrev så måste socketen initierats av klienten en gång i tiden.
     
    Last edited: 19 okt 2011
  15. foobar17

    foobar17 Kid Droid Medlem

    Blev medlem:
    12 jun 2009
    Inlägg:
    56
    Mottagna gillanden:
    11

    MINA ENHETER

    Ja, du skapar en WAR som du sen driftsätter i en Tomcat, vilket de flesta webbhotell har. Du kan också drifta det hos en moln leveratör såsom Amazon AWS eller svenska MyCityCloud. Fördelen med egen server är att du har 100% kolla på all konfigurering. Det innebär att du kan bygga upp en egen driftmiljö mha VirtualBox och sedan deploya till moln server, när du testat ut allt.

    En annan fördel med Grails är att du enkelt skapar en domän klass som en GroovyBean och placerar klassen i grails-app/domain/ katalogen.
    Kod:
    class Player {
        String name
        long  groupId
        ...
    }
    
    Resten sköts automagiskt, och du har sen en mapping i Hibernate fixat och en tabell skapad i MySQL.

    När det gäller client-server kommunikationen, skulle jag rekommendera pollning via HTTP från klienterna. På så sätt undviker du problem när en spelare åker in i en tunnel eller så. Anropet time:ar ut och sedan fortsätter clienten när det finns en anslutning igen.

    Grails har bra stöd för att rendera ett domän objekt som XML eller JSON. Det räcker med något i stil med följande
    Kod:
    def p = Player.get(params.id)
    render p as JSON
    
     
  16. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Ja, i stort sett. Det man måste hålla kollen på bara är vad man ska göra om/när connectionen bryts, vilket den tex görs när telefonen går ner på sparläge eller tappar uppkopplingen osv. Problemet med sockets i Android-Java är att det enda sättet att avgöra om en socket är öppen är att skicka nåt på den så jag skickar små PING-meddelanden med jämna mellanrum till servern. Om ett sådant ger ett error så försöker jag göra en reconnect.

    Men du kan nog glömma att försöka hålla socket connections öppna mha ett php-script. Du behöver nån form av dedicerad programvara för att hantera detta. Försöker du lösa detta med ett script på webservern så kommer det förr eller senare att timea ut (timeout bestäms ju av din webbhotellsleverantör) och då tappar du alla connections som du håller öppna
     
  17. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Varför begränsa sig till en apache http klient? Funkar väl lika bra med ett vanligt HttpClient httpclient = new DefaultHttpClient();


    Jag skulle föreslå, även om det inte är livsnödvändigt alltid, att du även slumpar fram nån form av sessionsID som du lagrar vid din användare i databasen. Detta skickar du också tillbaka till klienten som sen inkluderar det i kommande anrop till servern som då gör matchnings om de stämmer.

    Genom att klienten skickar SpelareID och SessionsID i anropen så kan du inte bara detektera vem spelaren är utan också huruvida den tillåts komma åt tjänsterna.

    En drawback med detta är förstås att man inte kan köra samma användare ifrån två enheter samtidigt... men det kanske också är önskvärt?
     
  18. uppdandom

    uppdandom Infant Droid Medlem

    Blev medlem:
    18 okt 2011
    Inlägg:
    10
    Mottagna gillanden:
    0

    MINA ENHETER

    Tack igen ReaPadda för ditt tålamod och dina svar. Det är till stor hjälp och jag uppskattar verkligen att du tar dig tid!

    Jag uttryckte mig lite klumpigt. Vi pratar om samma sak. Jag refererade bara till att det är apache-bibliotekets HttpClient och inte något annat externt biblioteks.

    Så om jag förstått det hela rätt utnyttjar du alltså sockets enbart för att kunna pusha användarna med info om att det finns data att hämta? Och användarna pingar servern via sin socket med jämna mellanrum för att servern ska ha en aktuell socket att kunna pusha till? Det vill säga om du vill pusha användaren med SpelarID = 2492 så finns en socket lagrad för denne, som hämtas och utnyttjas för att skicka pushen? Och denna push är ett komplement till pull-anropen via http-requests som användarna gör med jämna mellanrum?

    Om jag använder C2DM finns alltså egentligen ingen anledning att utnyttja sockets? Då tänker jag mig vid t ex ett drag gjort av en spelare att ordergången blir så här:

    1) En http-request skickas från klienten till servern
    2) Servern tar emot datan, uppdaterat speltillståndet, skapar ett svar till klienten som anropade (som den kan skicka tillbaka direkt som en response)och skapar även svar till andra berörda spelare som läggs i en "kö" (i t ex databasen?) av typen:

    Något SpelarID : data
    Något annat SpelarID: data

    3) Via C2DM meddelas övriga spelare om att det finns data att hämta. Dessa anropar då via en http-request. Scriptet kollar om det verkligen finns data att hämta via deras spelarid, hämtar det från databasen (om det är där köade datautskick lagras) och skickar ut det som response till dem.

    Rätt tänkt?
     
  19. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Låter som ett utmärkt upplägg.

    Vad gäller min socketlösning så använder jag den till mer än bara uppdateringsinfo. Den ligger till grund för chatten i klienten också plus att jag utifrån existerande socketconnections kan se "var" mina användare befinner sig i appen också.

    Så jag använder den inte BARA för att skicka updates-suggestions.
     
  20. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Och anledningen till att jag valt sockets för chatten och inte en lösning baserat på C2DM är för att varje match (man kan ha hur många som helst pågående parallellt i RummiFight) har sin egen chat och jag vill avlasta webbserver och databas maximalt. Därför har jag byggt en "MessageEngine" som cachar chattdatat i minnet så att allt ska gå blixtfort. Faktum är att om 1000 pers går inne i samma "chattrum" så accessas bara databasen en gång, såvida inte rummet redan är igång sedan förut, då accessas inte databasen alls.

    Hade jag valt C2DM även för chattmeddelandena så hade det antagligen krävts att klienten anropar webbservern (och därmed databasen) för att hämta meddelandehistoriken i rummet.

    Var knepigt att bygga, kan jag säga, men det funkar så varför gråta?