2012-05-25 17 views
5

Używam blokowania gniazd TCP dla mojego klienta i serwera. Zawsze, gdy czytam, najpierw sprawdzam, czy dane są dostępne w strumieniu za pomocą select. Zawsze czytam i zapisuję 40 bajtów na raz. Podczas gdy większość czytań zajmuje kilka milisekund lub mniej, niektóre z nich zajmują więcej niż pół sekundy. Po tym, jak wiem, że są dostępne dane na gnieździe.Co może być przyczyną bardzo wolnego odczytywania gniazd?

Jestem również za pomocą TCP_NODELAY

Co może być przyczyną tego?

EDIT 2

analizowałem znacznika czasu dla każdego pakietu wysłanych i odebranych i zobaczył, że opóźnienie to się dzieje tylko wtedy, gdy klient próbuje odczytać przedmiot przed następnym celem jest zapisywany przez serwer. Na przykład, serwer napisał obiekt numer x, a następnie klient próbował odczytać obiekt x, zanim serwer był w stanie rozpocząć pisanie obiektu numer x + 1. To sprawia, że ​​podejrzewam, że pewnego rodzaju koalescencja ma miejsce po stronie serwera.

EDIT

Serwer nasłuchuje na 3 różnych portów. Klient łączy się kolejno z każdym z tych portów.

Istnieją trzy połączenia: Jedna, która wysyła częste dane z serwera do klienta. Drugi, który wysyła dane tylko od klienta do serwera. Trzeci, który jest bardzo rzadko używany do przesyłania pojedynczego bajtu danych. Mam problem z pierwszym połączeniem. Sprawdzam przy użyciu select(), że dane są dostępne dla tego połączenia, a następnie, gdy znacznik czasu odczytuje 40 bajtów, stwierdzam, że około pół sekundy zostało zrobione dla tego odczytu.

Wszelkie wskazówki co do sposobu profilu To byłoby bardzo pomocne

używając gcc na linux.

rdrr_server_start(void) {
int rr_sd; int input_sd; int ack_sd; int fp_sd;

startTcpServer(&rr_sd, remote_rr_port); startTcpServer(&input_sd, remote_input_port); startTcpServer(&ack_sd, remote_ack_port); startTcpServer(&fp_sd, remote_fp_port);

connFD_rr = getTcpConnection(rr_sd); connFD_input = getTcpConnection(input_sd); connFD_ack= getTcpConnection(ack_sd); connFD_fp=getTcpConnection(fp_sd); }

static int getTcpConnection(int sd) { socklen_t l en;
struct sockaddr_in clientAddress; len = sizeof(clientAddress); int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len); nodelay(connFD); fflush(stdout); return connFD; }

static void startTcpServer(int *sd, const int port) { *sd= socket(AF_INET, SOCK_STREAM, 0); ASSERT(*sd>0);

// Set socket option so that port can be reused int enable = 1; setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = port; a.sin_addr.s_addr = INADDR_ANY; int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a)); ASSERT(bindResult ==0); listen(*sd,2); } static void nodelay(int fd) { int flag=1; ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); }

startTcpClient() { connFD_rr = socket(AF_INET, SOCK_STREAM, 0); connFD_input = socket(AF_INET, SOCK_STREAM, 0); connFD_ack = socket(AF_INET, SOCK_STREAM, 0); connFD_fp= socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = remote_rr_port; a.sin_addr.s_addr = inet_addr(remote_server_ip);

int CONNECT_TO_SERVER= connect(connFD_rr, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_input_port; CONNECT_TO_SERVER= connect(connFD_input, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_ack_port; CONNECT_TO_SERVER= connect(connFD_ack, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_fp_port; CONNECT_TO_SERVER= connect(connFD_fp, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

nodelay(connFD_rr); nodelay(connFD_input); nodelay(connFD_ack); nodelay(connFD_fp); }

+0

Mam wrażenie, że ten problem jest związany ze sprzętem ... – Jah

+0

** Jeden, który wysyła jakieś dane często z serwera do klienta **, jaki rozmiar jesteśmy rozmawiać o? – tuxuday

+0

Być może TCP_NODELAY (wyłączenie Nagle) jest złym wyborem, w wyniku czego wysyłanych jest wiele krótkich segmentów, co powoduje wielokrotne podróże w obie strony na "logiczny" pakiet. Plus wiele systemów po stronie aplikacji. – wildplasser

Odpowiedz

0

jeden klient i wiele połączeń?

niektóre funkcje gniazda mogą blokować wykonywanie (tzn. Oczekiwanie na wynik funkcji). Sugerowałbym otworzenie nowego wątku (po stronie serwera) dla każdego połączenia, aby nie przeszkadzali sobie nawzajem ...

ale kręcę w ciemności; musisz wysłać dodatkowe informacje ...

+0

Dodałem trochę szczegółów do mojego pytania. – AnkurVj

+0

Nie jestem pewien, czy twój problem polega na tym, ale prawdopodobnie należy zmienić kolejność funkcji startTcpServer/getTcpConnection. startTcpServer zawiera wywołanie funkcji listen(), która blokuje wykonywanie programu do momentu nawiązania nowego połączenia. Następnie powinieneś zaakceptować nowe połączenie i przejść dalej, ale masz jeszcze jedno połączenie do startTcpServer => inny odsłuch ... (wszystko w tym samym wątku) Więc zamiast przyjmować połączenie przychodzące, twoje gniazdo nasłuchuje (ale w innej funkcji) ... Może zmienisz je tak, jak: StartTcpServer(); getTcpConnection(); StartTcpServer(); getTcpConnection(); ? – Ivica

+0

Nie myślę bloków 'listen()'? Tylko 'accept()' robi IMO! – AnkurVj

0

Twoje oświadczenie jest nadal mylące, tj. "wiele połączeń TCP tylko z jednym klientem". Oczywiście masz jeden serwer nasłuchujący na jednym porcie. Teraz, jeśli masz wiele połączeń, oznacza to, że jest więcej niż jeden klient łączący się z serwerem, z którym każdy jest połączony z innym portem klienta tcp. Teraz serwer uruchamia wybieranie i odpowiada na dowolne dane klienta (co oznacza, że ​​klient wysłał dane na jego gniazdo). Jeśli dwaj klienci wysyłają dane jednocześnie, serwer może je przetwarzać tylko sekwencyjnie. Tak więc drugi klient nie zostanie przetworzony, dopóki serwer nie zostanie przetworzony najpierw.

Opcja Wybierz pozwala serwerowi na monitorowanie więcej niż jednego deskryptora (gniazd) i proces, który ma dostępne dane. Nie jest tak, że przetwarza równolegle. Do tego potrzebujesz wielu wątków lub procesów.

+0

Nie, serwer akceptował połączenia na trzech różnych portach od tego samego klienta. Idealnie każde połączenie powinno być całkowicie odizolowane od drugiego. – AnkurVj

+0

Czy możesz wkleić swój kod, który pokazuje tworzenie trzech różnych gniazd serwera i to jest przejście do wyboru? – fayyazkl

0

Być może jest to coś związanego z argumentem timeout.

Co ustawić dla timeout Argument select wywołanie?

Spróbuj zmienić parametr limitu czasu na większy i obserwuj opóźnienie. Czasami zbyt mały czas oczekiwania i bardzo często wywołania systemowe mogą w rzeczywistości zabić przepustowość. Może uda się osiągnąć lepsze rezultaty, jeśli przyjmiesz nieco większe opóźnienie, które jest możliwe do zrealizowania.

Podejrzewam przekroczenie limitu czasu lub jakiś błąd kodu.

+0

30 milisekund – AnkurVj

+0

Czy możesz wkleić kod z wywołaniem do 'select' i' read' z gniazda po 'select' zwraca? –

+0

@AnkurVj Dlaczego? To niesamowicie krótki czas oczekiwania. Może umierasz z głodu na procesorze. Limit czasu select() wynosi zwykle co najmniej kilka sekund. Co musisz zrobić, co musisz zrobić, co 30ms? – EJP

1

byłbym podejrzany o tej linii kodu:

ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); 

Jeśli używasz kompilacji uwolnienia, a następnie ASSERT jest prawdopodobnie najczęściej definiuje się niczego, więc nie byłoby rzeczywiście być wykonane połączenia. Wywołanie setsockopt nie powinno znajdować się w instrukcji ASSERT. Zamiast tego, wartość zwracana (w zmiennej) powinna zostać zweryfikowana w instrukcji assert. Asserts with side effects są generalnie złe. Więc nawet jeśli to nie jest problem, prawdopodobnie powinien zostać zmieniony.

+0

Och, faktycznie nie pokazałem części ASSERT zdefiniowanej w moim kodzie. To działa dobrze. – AnkurVj

+1

Ten kod jest nadal counter-idiomatyczny; umieszczenie kodu z efektami ubocznymi jako argumentem makra o nazwie "ASSERT" jest bardzo mylące dla czytelników, którzy prawdopodobnie zakładają, że nie działa on na kompilacjach bez debugowania. –

+0

@AnkurVj: Nawet jeśli uważasz, że wszystko jest w porządku, możesz spróbować usunąć połączenie z assert. Nie chodzi o to, że wątpię w twoje umiejętności, ale rzadko zdarza się, aby ktokolwiek prawidłowo zapisywał makro ASSERT za pierwszym razem. –

0

może spróbować użyć TCP_CORK (tryb CORK'ed) z rozszerzeniami jądra GRO, GSO i TSO wyłączone przez ethtool:

  • wysyłania wewnątrz TCP_CORK oznaczonej sesji zapewni, że dane nie zostaną wysłane w częściowym Segment

  • wyłączenie generic-segmentation-offload, generic-receive-offload i tcp-segmentation-offload zagwarantuje, że jądro nie wprowadzi sztucznych opóźnień zebrać dodatkowe Segmenty TCP przed przeniesieniem danych do/z przestrzeni użytkownika

Powiązane problemy