Hogy lehet egy programhoz írni szerver és kliens oldalt?
Pl most van egy program amit megírtam java nyelven ahhoz, hogy tudnék egy olyan kliens részt írni ami majd egy linux szerverről fogad adatokat.
Illetve olyan szerver oldalt, hogy lehet készíteni ami minden kliensnek továbít xy adatot.
És most ha a program java nyelven íródott akkor azt a részt ami majd az adatokat fogja fogadni a szervertől lehet írni jav a nyelven vagy valami más programnyelv szükséges ahoz, hogy a szerverrel kommunikáljom a kliens?
Ha lesz némi nagyon kis fogalmad a programozásról, meg egyáltalán a szerver-kliens struktúráról, akkor meg fogod kapni magadtól is a választ.
De láthatóan alapvetően óriási a káosz nálad ilyen téren.
Akartam is írni, hogy nyugodtan le lehet pontozni, attól mindjárt megvilágosodik a kérdező, és minden letisztul előtte biztosan.
Már eleve a kérdés is értelmetlen. Olyan nincs, hogy egy programhoz írunk klienst és szervert. Eleve már úgy kell megtervezni és megírni a programot, hogy legyen egy szerver, és egy kliens összetevője. (Oké, elviekben nem lehetetlen, hogy egy standard inputot és outputot használó programhoz írunk egyfajta "keretprogramot", ami rácsücsül a standard be- és kimenetre, és azt továbbítja egy szervernek... de ez így macerás, és nem is praktikus.)
A szerver-kliens kommunikáció pedig nem nyelvfüggő. Választasz valamilyen szabványos megoldást az adattovábbításra, és azzal a módszerrel átküldöd.
Sajnos nem minden esetben működik a nagy terv szerinti kliens-szerver architektúra kigondolása. Előfordulhat, hogy fel sem merül, mint igény a program tervezése során, mint például az általam fejlesztett kis zenelejátszóban.
Az első ötlet az volt, hogy akarok írni egy zenelejátszót. Egészen jól működött, meg lehetett benne nyitni zenét egy menüponttal, vagy drag&drop módszerrel rá lehetett dobni, de amikor fájlkezelőből indítottam el, akkor két példány indult el a programomból egyszerre két zenét játszva, ami nem volt túl jó. A lényeg az, hogy a program felismerje azt, hogy ő már fut egy példányban, és ha fut, akkor ahelyett, hogy új példányban elindulna, egyszerűen csak küldje el a már futó példánynak a zene fájlnevét, hogy az meg tudja nyitni. Erre két megoldást gondoltam ki:
1) Felhasználom a Windows API-ból a COPYDATASTRUCT-ot a SendMessage függvénnyel. Ezt azért nem akartam, mert gyakorlatilag függővé teszem a programomat az operációs rendszertől. (Nem mintha Windows-on kívül bárhol máshol használnám...)
2) Írok hozzá egy TCP szervert, ami JSON üzenetekkel tud kommunikálni a külvilággal. Ez azért is lesz jobb, mint a COPYDATASTRUCT-os megoldás, mert erre nem csak azok a programok tudnak kapcsolódni, amik ugyanazon a számítógépen futnak, hanem bármilyen más eszközzel, például egy mobiltelefonos alkalmazással is vezérelni tudom hálózaton keresztül. Előnye, hogy nagyon könnyen ki lehet deríteni, hogy fut-e már a program (ha tudsz kapcsolódni, akkor fut).
A programom bizonyos részeit (zenelejátszás, dalszöveg-fájlok kezelése, lejátszási lista kezelése, ...) elkülönítettem egymástól és feléjük interfészekkel egy absztrakciós réteget húztam. A szerver rész úgy lett elkészítve, hogy kérés-válasz alapon működjön, tehát maga a szerver magától soha nem fog adatot küldeni a kliensnek. Ellenben kialakításkor törekedtem arra, hogy moduláris legyen. Legyen egy modulja annak, amelyik a zenelejátszással kapcsolatos kérések feldolgozásával foglalkozik, legyen egy modulja, ami a dalszöveg-kezeléssel, stb.
A C# 6-os verziójában írom a programot, ott valahogy így néz ki a dolog:
interface IServerModule {
.. string ModuleName { get; }
}
//Ezzel azt akarjuk elérni, hogy csak az ezzel az attribútummal megjelölt
//metódusokat lehessen meghívni az osztályon belül.
class ServerCommandAttribute : Attribute {...}
class PlaybackModule : IServerModule {
.. public string ModuleName { get; } = "Playback";
.. public IPlaybackProvider Provider { get; set; } = new NullPlaybackProvider();
.. [ServerCommand]
.. public bool OpenFile(string Path) => return this.Provider?.OpenFile(Path) ?? false;
.. [ServerCommand]
.. public bool IsPlaying() => return this.Provider?.Playing ?? false;
.. [ServerCommand]
.. public void SetVolume(string Value) => this.Provider?.Volume = Convert.ToInt32(Value);
}
És végül van a PlayerServer osztály, ami maga a szerver része a programnak:
class PlayerServer : IDisposable {
.. private readonly ICollection<IServerModule> Modules = new List<IServerModule>();
.. public void AttachModule(IServerModule Module) => this.Modules.Add(Module);
.. public PlayerServer(bool LocalhostOnly, int Port) { ... }
.. private void ListenerThread() {
.. .. //Ezt a konstruktorban létrehozott Thread hajtja végre. Amint egy
.. .. //új kliens kapcsolódik, indít egy új szálat, amely a ClientThread-et hajtja végre.
.. .. //A szálakat a ThreadPool-ból szedem.
.. }
.. private void ClientThread(object ClientStreamAsObject) {
.. .. //Annyit csinál, hogy adatra vár a klienstől. A kliens által küldött adat három
.. .. //részből áll: a modul neve, a metódus neve (ami ServerCommand attribútummal rendelkezik),
.. .. //illetve a paraméterek, mint string tömb. Reflection-t használva meghívja a
.. .. //megfelelő modul megfelelő metódusát, majd visszaküldi az eredményt.
.. }
.. public void Dispose() {
.. .. //Leállít minden elindított szálat és leállítja a szervert.
.. }
}
És a kérdés, hogy ez a szerver hogyan kel életre.
class MainWindow : Form {
.. //A hozzájuk tartozó mezőket most nem írom ide. De vannak.
.. private IPlaybackManager Playback {
.. .. get { return this.playback; }
.. .. set {
.. .. .. this.playback = value;
.. .. .. this.ServerPlaybackModule.Provider = value;
.. .. }
.. }
.. //Hasonlóan a többinél.
.. private IPlaylist Playlist { get... set... };
.. private ILyricsReader LyricsReader { get... set... };
.. private readonly PlayerServer PlayerServer;
.. private readonly PlaybackModule ServerPlaybackModule;
.. //Igazából meg lehetne csinálni azt is, hogy ne virítson itt minden olyan szerver modul,
.. //amit piszkálni kellene a program működése során, de így egyszerűbb volt. A gond elvégre az, hogy a modulokat cast-olgatni
.. //kellene és kellene egy IServerModule PlayerServer.GetModule(string Name) metódus is... így egyszerűbb volt.
.. public MainWindow() {
.. .. //Elindítjuk magát a szervert
.. .. this.PlayerServer = new PlayerServer(LocalhostOnly: true, Port: 22613);
.. .. //Létrehozunk és beállítunk egy modult
.. .. this.ServerPlaybackModule = new PlaybackModule();
.. .. //Felcsatoljuk a modult a szerverre
.. .. this.PlayerServer.AttachModule(this.ServerPlaybackModule);
.. .. //Nem szabad elfelejteni, hogy a szervert le kell állítani, ha a program véget érne.
.. }
}
"Egy programhoz" nem kell se szerver- se kliensoldalt írni.
Adott program lesz maga a szerver (ha úgy van megírva), a másik, hozzá tartozó meg a kliens.
Kapcsolódó kérdések:
Minden jog fenntartva © 2024, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!