Jeden socket bidirectional czy dwa osobne sockety?

0

Załóżmy że mam do napisania system czas rzeczywistego w którym poszczególne komponenty komunikują się przez sieć (będzie to komunikacja peer-to-peer po UDP). Komponenty muszą wysyłać nawzajem do siebie ciągły strumień danych. Czy w tym przypadku lepiej skorzystać z jednego socket'u i tam naprzemiennie wysyłać/odbierać paczki (a może sockety umożliwiają wysyłkę/obiór paczek jednocześnie?), czy może między komponentem A i B otworzyć dwa socket'y, jeden służący do wysyłki strumienia danych od A do B, drugi służący do wysyłki strumienia danych od B do A, a może nie ma to żadnego znaczenia ze względów wydajnościowych? Dodam, że strumień danych, który wysyła komponent A do komponentu B jest całkowicie niezależny od strumienia który wysyła komponent B do A.

4

Jak będziesz miał dwa wątki i dwa różne sockety to na pewno to będzie bardziej wydajne niż jeden socket na jednym wątku

0

Z innych socketów jest wątpliwe, czy będzie przechodzić przez firewalle ... o ile to jest taki case

Po drugie, nie wyobrażam sobie socketa zupełnie jednokierunkowego. A gdzie handshaking?

2

Dwa sockety zdecydowanie lepiej w tym przypadku.

1

A, a może nie ma to żadnego znaczenia ze względów wydajnościowych?

Raczej nie ma znaczenia. Zwłaszcza jeśli zastosujesz jakiś event loop. Wydajnościowo powinno być tak samo, bo i tak raczej będzie Ciebie ograniczać IO, a nie czas CPU. Ja bym użył jednego socketa, bo łatwiej i wygodniej to w taki sposób zrobić (zwłaszcza z event loopem).

2

Nie rozumiem przedmówców. Możesz m.in. stworzyć jeden socket, i jednocześnie czytać i pisać do niego z dwóch wątków.

0

@enedil: własnie widziałem w dokumentacji MSDN: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket?view=netcore-3.1, że klasa Socket jest thread-safe, zastanawia mnie jednak jak to wygląda od strony wydajnościowej, jeżeli mogę jednocześnie czytać i pisać z jednego socketu to będzie to tak samo wydajne jak dwa osobne sockety w osobnych wątkach?

@hauleth: mógłbyś sprecyzować co masz na myśli pisząc event loop, czy chodzi Ci po prostu o użycie asynchronicznych metod do wysyłki/odbioru tak, żeby nie blokować programu?

1

mógłbyś sprecyzować co masz na myśli pisząc event loop, czy chodzi Ci po prostu o użycie asynchronicznych metod do wysyłki/odbioru tak, żeby nie blokować programu?

Nie napisałeś w jakim języku pracujesz, więc możliwe, że "asynchronicznych metod" nie ma. Można tutaj np. słuchać na jednym sockecie, ale faktyczną pracę robić już w osobnych wątkach i to z tych wątków odpowiadać. Wszystko zależy co i jak chcesz osiągnąć, oraz jakie narzędzia daje Ci sam język.

2

Jeśli chodzi o wydajność, to niestety nie wiem, moja odpowiedź była bazowana na doświadczeniu z Linuksem (i nieco przypadkiem się zdarzyło, że na Windowsie też działa). W kwestii event loop, są biblioteki, które upraszczają synchroniczne operacje na sieciach - możesz jednocześnie czekać na możliwość przeczytania i wysłania danych, i gdy zdarzy się event (np. że możesz czytać), to uruchamiany jest zarejestrowany przez Ciebie handler. Przykładami takich bibliotek jest libevent czy libuv (dla C/C++, ale na nich też bazują implementacje w innych językach).

3

Wydajność
Uważam to za niepoprawne stwierdzenie, że dwa sockety na dwóch wątkach są bardziej wydajne nie mając szerszego kontekstu. Czy dane wysyłane i odczytywane są synchronizowane ze sobą? W takim wypadku być może posiadanie 2 gniazd na 2 wątkach (czy też 2 wątków i 1 socketa) jedynie pogorszy wydajność , bo dojdzie mechanizm synchronizacji pomiędzy nimi. Podczas gdy epoll/poll/select/kqueue na jednym gnieździe wyeliminuje konieczność synchronizacji pomiędzy wątkami, bo całość danych będzie modyfikowana tylko z jednego wątku.
Kolejną kwestią jest wąskie gardło. Czy są to obliczenia czy być może wejście/wyjście? Jeżeli jest to wejście/wyjście, to dwa wątki nie pomogą, nie przyśpieszą komunikacji sieciowej. Dwa wątki mogą przyśpieszyć kolejkowanie i konsumowanie danych z bufora jądra systemu (de facto nieblokujące gniazda zapewnią to samo), pytanie jednak czy nie jest tak, że i/o karty sieciowej jest wolniejsze od samego przetwarzania już otrzymanych danych i buforowania nowych do wysłania.
Czy wiesz ile czasu potrzeba na przetworzenie otrzymanych danych? Jeżeli mówimy o szybkich operacjach przetwarzania, to 1 nieblokujący socket może okazać się wydajniejszy.
Można też zadać sobe pytanie czy dane są przetwarzane od razu, czy są kolejkowane i przetwarzane później przez jeszcze inny wątek?

Język programowania
Jaki model komunikacji sieciowej oferuje język programowania? Niskopoziomowe operacje na socketach? Model aktorów? Pętla zdarzeń? Nie ma co walczyć z językiem.

0
nalik napisał(a):

Wydajność
Czy dane wysyłane i odczytywane są synchronizowane ze sobą? W takim wypadku być może posiadanie 2 gniazd na 2 wątkach (czy też 2 wątków i 1 socketa) jedynie pogorszy wydajność , bo dojdzie mechanizm synchronizacji pomiędzy nimi. Podczas gdy epoll/poll/select/kqueue na jednym gnieździe wyeliminuje konieczność synchronizacji pomiędzy wątkami, bo całość danych będzie modyfikowana tylko z jednego wątku.

Dane wysyłane przez komponent A do B są niezależne od danych wysyłanych od komponentu B do A. W takim razie synchronizacja nie jest wymagana.

Kolejną kwestią jest wąskie gardło. Czy są to obliczenia czy być może wejście/wyjście? Jeżeli jest to wejście/wyjście, to dwa wątki nie pomogą, nie przyśpieszą komunikacji sieciowej. Dwa wątki mogą przyśpieszyć kolejkowanie i konsumowanie danych z bufora jądra systemu (de facto nieblokujące gniazda zapewnią to samo), pytanie jednak czy nie jest tak, że i/o karty sieciowej jest wolniejsze od samego przetwarzania już otrzymanych danych i buforowania nowych do wysłania.

Zbieranie informacjii odbywa się z wykorzystanie jedynie RAM i procesora.

Czy wiesz ile czasu potrzeba na przetworzenie otrzymanych danych? Jeżeli mówimy o szybkich operacjach przetwarzania, to 1 nieblokujący socket może okazać się wydajniejszy.
Można też zadać sobe pytanie czy dane są przetwarzane od razu, czy są kolejkowane i przetwarzane później przez jeszcze inny wątek?

Odebrane dane będą od razu prezentowane na GUI.

Język programowania
Jaki model komunikacji sieciowej oferuje język programowania? Niskopoziomowe operacje na socketach? Model aktorów? Pętla zdarzeń? Nie ma co walczyć z językiem.

C#, coraz bardziej jestem przychylny implementacji opartej na jednym socket z wykorzystaniem UdpClient, bardzo uproszczony komunikator dla testów już naskrobałem:

public class UdpCommunicator : ICommunicator, IDisposable
    {
        private UdpClient _client;
        private Receiver _receiver;
        private readonly IPEndPoint _endpoint;

        public event EventHandler<DataReceivedEventArgs> DataReceived;

        public UdpCommunicator(IPEndPoint endpoint)
        {
            _endpoint = Guard.Argument(endpoint).NotNull();
        }

        public void Dispose()
        {
            if (_client != null)
            {
                _client.Dispose();
                _client = null;
            }
        }

        public Task Connect()
        {
            _client = new UdpClient(_endpoint.Port);

            _receiver = new Receiver(_client);
            _receiver.DataReceived += OnDataReceived;

            return Task.CompletedTask;
        }

        public Task Send(byte[] bytes)
        {
            Guard.Argument(bytes).NotNull().NotEmpty();

            return _client.SendAsync(bytes, bytes.Length, _endpoint);
        }

        private void OnDataReceived(object sender, DataReceivedEventArgs e) => DataReceived?.Invoke(this, e);

        private class Receiver
        {
            private readonly UdpClient _client;
            internal event EventHandler<DataReceivedEventArgs> DataReceived;

            internal Receiver(UdpClient client)
            {
                _client = client;
                Task.Run(async () => await Run());
            }

            private async Task Run()
            {
                while (true)
                {
                    var result = await _client.ReceiveAsync();
                    DataReceived?.Invoke(this, new DataReceivedEventArgs() { Data = result.Buffer });
                }
            }

        }
    }

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