2013-07-21 12 views
9

Chcę utworzyć połączenie nieblokujące. jak poniżej:Linux, gniazda, nieblokujące połączenie

socket.connect(); // returns immediately 

W tym celu użyć innej nici, nieskończoną pętlę, i Linux epoll. Jak to (Pseudokod):

// in another thread 
{ 
    create_non_block_socket(); 
    connect(); 

    epoll_create(); 
    epoll_ctl(); // subscribe socket to all events 
    while (true) 
    { 
    epoll_wait(); // wait a small time(~100 ms) 
    check_socket(); // check on EPOLLOUT event 
    } 
} 

Gdybym uruchomieniu serwera, a następnie klienta, wszystko działa. Jeśli najpierw uruchomię klienta, poczekaj chwilę, uruchom serwer, a klient się nie połączy.

Co robię źle? Może można to zrobić inaczej?

+0

Jeśli przenosisz inny wątek, aby wykonać połączenie, dlaczego robisz to asynchronicznie? Równie dobrze może umieścić tam resztę komunikatorów. –

+0

Cóż, jak to zrobić bez epolacji i bez blokowania? Jeśli po prostu zadzwonię connect(), to zablokuje i zaczeka na połączenie (mam rację?). Ale jeśli chcę dołączyć do tego wątku łączącego z głównym wątkiem, nie mogę tego zrobić, ponieważ wątek połączenia będzie w stanie blokowania. Przepraszam, jeśli się mylę. – herolover

+1

To nie jest "asynchroniczne". To nie jest blokowanie. – EJP

Odpowiedz

28

Należy stosować następujące kroki w async połączyć:

    gniazdo
  • tworzyć z socket(..., SOCK_NONBLOCK, ...)
  • połączenie startu z connect(fd, ...)
  • jeśli zwracana wartość nie jest ani 0 ani EINPROGRESS, a następnie przerwać z powodu błędu
  • poczekaj, aż fd zostanie zasygnalizowane jako gotowe do wyprowadzenia
  • sprawdź sta tus gniazda z getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)
  • zrobić

Brak pętli - chyba że chcesz obsługiwać EINTR.

Jeśli klient został uruchomiony jako pierwszy, w ostatnim kroku powinien zostać wyświetlony błąd ECONNREFUSED. Jeśli tak się stanie, zamknij gniazdo i zacznij od początku.

Trudno powiedzieć, co jest nie tak z kodem, nie widząc więcej szczegółów. Przypuszczam, że nie przerwiesz błędów w operacji check_socket.

+0

Wiem, że to stary komentarz, ale chciałem tylko zauważyć, że musiałem czekać na przeczytanie, aby złapać ETIMEDOUT. Stało się tak, gdy odpowiedź SYN nie została zwrócona. Gdybym tylko czekał na zapis, to gniazdo zniknąłoby z netstat (ze stanu SYN_SENT), ale nie otrzymałbym powiadomienia, że ​​gniazdo było do zapisu, aby wywołać getsockopt i znaleźć ETIMEDOUT. Dodałem także połączenie natychmiast po połączeniu się z getsockopt, aby sprawdzić, czy przed odpytywaniem pojawiły się jakieś natychmiastowe błędy. – DreamWarrior

+0

@DreamWarrior: To dziwne. Spójrz na [connect (2)] (http://linux.die.net/man/2/connect) i [connect (3)] (http://linux.die.net/man/3/connect) i wyszukaj 'poll'. Obie strony man stwierdzają, że powinieneś poczekać na wskazanie, że _socket_ jest _writable_. Czy możesz prodive przykład minimal, który pokazuje nieoczekiwane zachowanie? – nosid

+0

strona podręcznika "stwierdza, że ​​można wybrać (2) lub odpytać (2), wybierając gniazdo do zapisu". Domyślam się, że kluczowym słowem jest "ukończenie". Ponieważ nigdy nie został ukończony, ponieważ nigdy nie otrzymał SYN-ACK (lub RST, który kończy uzgadnianie, ale powoduje awarię), nigdy nie został zapisany. – DreamWarrior

Powiązane problemy