Komunikacja między dwoma aplikacjami na tym samym komputerze

0

Cześć,

słuchajcie, jak przekazać trochę danych (jednego stringa) między dwoma aplikacjami na tym samym komputerze? Obydwie aplikacje będą mojego "pióra" więc rozchodzi mi tylko o to jakiej technologii użyć?
Dane powinny być przekazywane w miarę online, system powinien być odporny na błędy związane z brakiem aplikacji docelowej.
Myślałem o komunikatach, socketach tcp czy lepiej UDP, plikach tekstowych (to najmniej mi się podoba).
Powinno to być lekkie, bo w końcu to prosta sprawa (niby).
Coś zasugerujecie?

8

Czy zawsze będą na tym samym kompie, czy jest później opcja rozdzielenia?
Czy będzie to zawsze 1:1 czy np. jest opcja jednego serwera i kilku klientów?
Jak długie będą te komunikaty?
Czy potrzebujesz potwierdzeń otrzymania/nic nie może się zgubić, czy jak jakiś komunikat przepadnie to nic się nie stanie?

5
cerrato napisał(a):

Czy zawsze będą na tym samym kompie, czy jest później opcja rozdzielenia?
Czy będzie to zawsze 1:1 czy np. jest opcja jednego serwera i kilku klientów?
Jak długie będą te komunikaty?
Czy potrzebujesz potwierdzeń otrzymania/nic nie może się zgubić, czy jak jakiś komunikat przepadnie to nic się nie stanie?

Brakło jeszcze pytania jaki system, bo jak linux/unix to mozna użyć Unix domain socket.
Z tak niewielnik opisam, to jakbym miał zaklepać na szybko komunikacje to użyłbym RESTa bo to znam najlepiej :P
Jakbym miał wiecej czasu to poczytałbym o ZeroMQ bo już dawno chciałem to przetestować. Chyba nawet jest jakiś binding dla pascala

3

Jeśli Windows - ja do tego używam komunikacji opartej na bazie WM_COPYDATA. Prościej się chyba nie da.

3

Jak komunikacja request response to HTTP. Jak komunikaty i trzymanie połączenia to websockety. Jak chcesz oba na raz i lubisz protobufy to gRPC. Tak czy owak: normalne rozwiązania, których używamy pomiędzy aplikacjami na różnych maszynach. Inne rozwiązania mają sens jak te trzy niedomagają wydajnościowo.

3

Jak Windows to moze stary dobry (bo wypróbowany) sposób https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory (w C++ ale nie ma żadnego problemu aby to zrobić w Delphi)
Oczywiście można użyć socketów (TCP/IP lub UDP nie żadne HTTP jak ktoś proponował) ale może wystąpić problem zajętych portów itp.
WM_COPYDATA - wymaga znalezienia uchwytu okna aplikacji co nie zawsze jest proste (np. FreePacal w odróżnieniu od Delphi zdaje sie nie bardzo pozwala na zmianę klasy okna).

1

WM_COPYDATA - wymaga znalezienia uchwytu okna aplikacji co nie zawsze jest proste (np. FreePacal w odróżnieniu od Delphi zdaje sie nie bardzo pozwala na zmianę klasy okna).

A czasem aplikacja nie ma okna dlatego warto sobie napisać prostą bibliotekę/komponent tworzący niewidoczne okno najlepiej nietypowej klasy (RegisterClass). To pozwala na połączenie nawet między aplikacjami konsolowymi.
Użycie socketów ma tą wadę że czasem burzą się firewalle. No i systemowy komunikat w zasadzie wymusza drugą aplikację działającą na tym samym systemie. Ma to tak samo wady jak i zalety, zależnie od potrzeb.

3

FreePacal w odróżnieniu od Delphi zdaje sie nie bardzo pozwala na zmianę klasy okna

Ale po co masz zmieniać klasę, żeby dogrzebać się do uchwytu okna?

1
cerrato napisał(a):

FreePacal w odróżnieniu od Delphi zdaje sie nie bardzo pozwala na zmianę klasy okna

Ale po co masz zmieniać klasę, żeby dogrzebać się do uchwytu okna?

Aby mieć jakąś unikalną a nie standardowy TForm co pozwoli w bardziej pewny sposób znaleźć i pobrać uchwyt okna.

0

Precyzując:

  • aplikacja główna to POS,
  • drugi monitor to reklama (strona www) oraz pasek informacyjny (jak w drukarce fiskalnej),
  • obydwie aplikacje uruchomione zawsze na tym samym komputerze,
  • środowisko to Windows,

Przesyłany będzie string o długości 40 znaków, nic więcej. Nie potrzebuje żadnego potwierdzenia że doszedł. Program wysyłający nie może nawet kichnąć w trakcie wysyłki (czyli coś lekkiego) i musi być odporny na brak programu odbierającego.

Na teraz najbardziej jestem skłonny do UDP, prosta konfiguracja, odporność na brak serwera. Niby Indy ale już jest używane w programie wysyłającym do komunikacji z innym programem (już sieciowo).
REST-a też bardzo lubię.

Nie znałem WM_COPYDATA - już zerkam.

2

Aby mieć jakąś unikalną a nie standardowy TForm co pozwoli w bardziej pewny sposób znaleźć i pobrać uchwyt okna.

Ok, jest to jakieś wytłumaczenie. Ale zauważ, że autor wątku pisze, że obie strony gadające ze sobą są jego autorstwa, więc ja bym raczej, zamiast bazować na jakiejś nazwie klasy, dodał jakiś sposób komunikacji/zapoznania się partnerów ze sobą. Chociażby jakiś broadcast UDP, albo jeszcze prościej - skoro sugerujemy zrobić komunikację w oparciu o WM_COPYDATA to wysłać jakiś tekst powitalny do wszystkich okien z taką klasą. Niczym to nie grozi, po prostu - jeśli dane okno tak się nazywa, ale nie jest "kompatybilne" z naszym projektem, to zignoruje powitanie. A jeśli jest chętne do współpracy to odpowie i wtedy już wiemy kto ma z kim gadać.

Przesyłany będzie string o długości 40 znaków, nic więcej. Nie potrzebuje żadnego potwierdzenia że doszedł

Tutaj opcje są dwie - albo ten UDP, co na 99% będzie dochodzić i działać, albo wspomniany komunikat WM_COPYDATA. Przy czym mam wrażenie, że pewniejsze są komunikaty - bo jakakolwiek komunikacja sieciowa może zostać zakłocona, jakiś firewall albo kłopot z ustawieniami karty/konflikt adresów/cokolwiek i masz problem. A komunikaty muszą działać zawsze i wszędzie, stanowią one podstawę działania Windowsa. Jedyny minus że jakby jednak pojawiła się za jakiś czas potrzeba rozdzielenia tych dwóch części na dwie maszyny to wtedy musisz zmienić mechanizm, bo komunikaty działają tylko w obrębie jednego systemu

musi być odporny na brak programu odbierającego

Oba zaproponowane rozwiąznia tak działają - czy to wyślesz komunikat, czy UDP to zasadniczo wysyłasz w świat i potem już nic Cię nie interesuje. Nie jest to list polecony z potwierdzeniem odbioru, gdzie dostajesz żółtą zwrotkę i masz pewność, że odbiorca dostał. To raczej odpowiednik otwarcia okna i krzyknięcia czegoś na ulicę. Nie masz pojęcia czy ktokolwiek to usłyszał. Otwierasz, krzyczysz, zamykasz i nie wiesz co się potem dzieje ;)

REST-a też bardzo lubię.

Biorąc pod uwagę Twój opis potrzeb i sposobu działania, to mamy piękny przykład przysłowiowej armaty na komara :P

2

chyba jednak zrobię to przez UDP. Kurde, jedna linijka po każdej stronie i sprawa załatwiona. Konfiguracja też do bólu prosta. Co będę kombinował.
dzięki za dyskusję.

2

Tylko pamiętaj, że to UDP jest ryzyko, że zostanie zablokowane. Jak to ustawisz raz (jakieś wpisy na firewallu itp) to potem będzie działać, ale ktoś coś naklika albo wejdzie jakaś aktualizacja systemu i jest ryzyko, że się wywali. OK, naprawienie tego to chwila, a szanse niewielkie - ale jednak jest to mniej pewne rozwiązanie niż bazowanie na komunikatach.

2
robertz68 napisał(a):

Cześć,

słuchajcie, jak przekazać trochę danych (jednego stringa) między dwoma aplikacjami na tym samym komputerze? Obydwie aplikacje będą mojego "pióra" więc rozchodzi mi tylko o to jakiej technologii użyć?
Dane powinny być przekazywane w miarę online, system powinien być odporny na błędy związane z brakiem aplikacji docelowej.
Myślałem o komunikatach, socketach tcp czy lepiej UDP, plikach tekstowych (to najmniej mi się podoba).
Powinno to być lekkie, bo w końcu to prosta sprawa (niby).
Coś zasugerujecie?

Jeśli to ma być na Windows to: https://en.wikipedia.org/wiki/Named_pipe#In_Windows

Ta dwie aplikacje mają działać na tym samym pulpicie? (w sensie tradycyjnym Windows NT 4.0 TS, nie chodzi mi o wirtualny pulpit Windows 10)

edit: pamiętaj, że UDP nie gwarantuje "doręczenia" komunikatu, nawet na localhoscie

0

Skoro obie aplikacje są pisane przez OP i obie działają na tym samym komputerze, to pchanie się w rozwiązania sieciowe jest co najmniej dziwne. Sam system posiada rozwiązania do zapewnienia pewnej i sprawnej wymiany danych między procesami. Sam bym celował w shared memory/file mapping.

3

Wg mnie, najprościej użyć WM_CopyData. Proste i działa.
Coś takiego powinno przesłać tekst z 1 apki do drugiej (nadawca musi znaleźć okno odbiorcy)

// SENDER APPLICATION
function Set_Output_Text(AText: string): string;
var
  ACopyData    : TCopyDataStruct;
  AData        : UnicodeString;
  AWndHandle   : HWND;
begin
   AWndHandle := FindWindow('TUniqueFormName', nil);
   if AWndHandle = 0 then
      begin
         result := '-1'; // No handle!
      end
   else
      begin
         ZeroMemory(@ACopyData, SizeOf(ACopyData));
         ACopyData.dwData := 123456; // Unique Number
         ACopyData.cbData := 2 * Length(AText);
         ACopyData.lpData := PWideChar(AText);
         result := IntToStr(SendMessageW(AWndHandle, WM_COPYDATA, 0, LPARAM(@ACopyData)));
      end;
end;


// RECEIVER APPLICATION
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes;
type
  TUniqueFormName = class(TForm)
// ...

  protected
    procedure WMCopyData(var Message: TWMCopyData); message WM_COPYDATA;
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  

implementation

// WMCopyData
procedure TUniqueFormName.WMCopyData(var Message: TWMCopyData);
var
   S: UnicodeString;
begin
   if Message.CopyDataStruct^.dwData = 123456 then // is the message from the Writer app?
      begin
         SetString(S, PWideChar(Message.CopyDataStruct^.lpData), Message.CopyDataStruct^.cbData div SizeOf(WideChar));
         // S variable is our data!
      end;
end;

-Pawel

0

@Pepe: Tylko pytanie, czy te aplikacje są na tym samym pulpicie.

Nie zapominajmy, że Windows NT jest systemem wielodostępnym, tylko ta wielodostępność jest w wersjach nieserwerowych mocno okrojona.

Ale jeśli to jest Windows Server z RDS, a aplikacje są uruchomione w różnych sesjach, to WM_COPYDATA najpewniej nie zadziała.

0

Ale jeśli to jest Windows Server z RDS, a aplikacje są uruchomione w różnych sesjach, to WM_COPYDATA najpewniej nie zadziała.

OP pisał gdzieś, że ten komp to jakiś POS, więc pewnie zwykły Windows desktopowy z odpalonym jednym loginem fizycznie - żadne RDP czy inne terminale. Raczej tutaj bym nie szukał potencjalnego fakapu.

0

jak na tym samym kompie ma to sie odbywać to może offline po prostu string sie bedzie zapisywał do pliku TXT i drugi program go będzie odczytywał i tyle

1

To tylko moja prywatna opinia, ale uważam że sockety i protokół TCP/IP lub UDP są najlepszymi narzędziami do komunikacji między aplikacjami.
To są narzędzia uniwersalne dające możliwość międzyplatformowej wymiany pomiędzy różnymi aplikacjami działającymi na dowolnych hostach

0

@_13th_Dragon:
nie chrzanię i znam temat postu
PS. Omyłkowo zamiast zedytować komentarz usunąłem mój cały post,

0

Wystarczy SharedMemory.
Mapujesz na rekord:
packed record data:byte; next:boolean; end;
nadawca - wpisuje w data kolejny bajt danych i ustawia: next:=true; i czeka na next=false
odbiorca - czeka na next=true wczytuje bajt danych i ustawia: next:=false;
po obu stronach wątek ze strumieniami.

0

Trochę to z tyłka. Przekazywanie danych bajt po bajcie to najwolniejsze rozwiązanie, jakie tylko można sobie wyobrazić. W dodatku pakowanie struktury jeszcze bardziej spowolni cały proces ().

Równie dobrze można użyć zwykłego wskaźnika dla danych. Jedna strona alokuje blok pamięci i wpisuje tyle danych ile trzeba przekazać. Druga strona odbiera te dane i kopiuje lub robi co tylko chce. Strona przekazująca dane czeka aż wskaźnik będzie nilem, a następnie alokuje pamięć i wpycha dane. Strona odbierająca czeka aż wskaźnik będzie różny od nil, a następnie wykorzystuje przekazane dane i na koniec ustawia wskaźnik na nil.

Nie dość, że będzie to działać znacznie szybciej, to w dodatku wystarczy tylko jedna zmienna, czyli wskaźnik dający dwie informacje. Pierwszą jest adres jako zielone lub czerwone światło, drugą jest bufor danych, na który wskazuje.

Obstawiam, że da się to nawet zrównoleglić, czyli jedna strona może generować dane, podczas gdy druga je w tym samym czasie odczytywać. Oczywiście nie w formie jednego, wspólnego bloku pamięci, bo tak to się nie da. Jeśli jedna strona wygeneruje dane, druga może sobie skopiować adres wskaźnika i zmienną ustawić na nil, aby od razu dać zielone światło tej pierwszej, żeby generowała kolejny pakiet danych, a w tym czasie strona druga sobie kopiuje/używa to co dostała.

W ten sposób bym kombinował.

1

Chyba zapomniałeś że jedna strona zaalokowała zaś druga pod tym adresem nic nie widzi bo ma inną adresacje wirtualną.

0

Ów adres nie musi być traktowany jako faktyczny, natywny wskaźnik — może być adresem liczbowym, wysyłanym w zwykłym komunikacie. Natomiast named shared memory pozwala za pomocą jednego obiektu wymieniać bufory danych o dowolnym rozmiarze. Można zarezerwować obiekt o rozmiarze dwóch paczek danych, tak aby jedna strona zapisywała dane w jednej części, a druga czytała w tym czasie z drugiej części. A potem flip jak w double-bufferingu GPU, i tak w kółko.

Natomiast sprawdzanie tego która strona ma działać, a która czekać, powinno być raczej zaimplementowane nie na podstawie zawartości bufora (jak u Ciebie na podstawie pola next), a za pomocą odpowiednich mechanizmów synchronizacyjnych. Myślę, że muteksty by się nadały, bo są widoczne w obrębie całego systemu.

1

@furious programming widzę że już zrozumiałeś że adres i nil nie zadziała, to dobrze.
Więc proponujesz mutex'a zamiast zwyklej zmiennej boolean doprawdy?
Przypominam że masz dostawca-odbiorca 1:1

0

Przeczytałem ostatnie 4 posty, więc nie wiem jakie propozycje padły wcześniej. Prosty sposób jaki zrobiłem daaaawno temu "na szybko" polegał na zastosowaniu zwykłego pliku tekstowego który był osią wymiany informacji pomiędzy dwoma różnymi programami.
W razie czego nie bić, chciałem pomóc na szybko... :)

0

@_13th_Dragon: i co, dostawca i odbiorca mają rżnąć CPU (czyli wykonywać absolutnie bezsensowny busy waiting), w ten sposób czekając na to aby zapisać/odebrać dane? Po to właśnie wymyślono mechanizmy synchronizacyjne jak choćby muteksy/semafory i WaitForSingleObject (przy okazji z timeoutami), aby takich głupot nie robić. Nie dość, że sugerujesz marnowanie mocy CPU na niebezpieczną bieda-synchronizację, to jeszcze proponujesz wymianę danych po jednym bajcie — jedno i drugie jest najgorszym z możliwych rozwiązań.

No ale spoko, nie będę się wykłócał.

1 użytkowników online, w tym zalogowanych: 0, gości: 1