2016-03-27 21 views
0

Bu soru daha önce sorulmuşsa özür dilerim. Seçimi çoğullama kullanarak engelleme olmayan bir yuva istemcisi yazıyorum. Kafamı karıştıran bir şey, engellemesiz bağlantı sunucunun çevrimiçi veya çevrimdışı olmasına bakılmaksızın her zaman başarılı olur. Birçok gönderiyi aradım ve çözümlerini takip ettim, ancak hiçbiri linux ubuntu makinemde çalışmadı. seçme döner ve her zaman başarılı bu geri çağırır, yani gidiş "Hazır/yazma okuma" yerine başarısızlık cerring arasında, blokTıkanmasız Soket bağlantısı her zaman başarılı olur?

static void callback_on_select_write(int connect_fd) { 

    // Client write event arrived; 

    int error = -1; 
    socklen_t len = sizeof(error); 

    if(getsockopt(connect_fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { 
    return; 
    } 

    // getsockopt puts the errno value for connect into erro so 0 means no-error. 
    if(error == 0) { 
     // Connection ok. 
    } 
    else { 
    cerr << "Failed to connect\n"; 
    return; 
    } 

    // Ready to write/read 

} 

Her zaman. Bu neden olabilir? Bağlantının gerçekten başarılı olup olmadığını anlamak için taşınabilir bir mekanizmayı nasıl tasarlarım? Aşağıda bağlayıcıyı oluşturmuyorum. seçme döner ve her zaman başarır bu geri çağırır, yani gidiş "Hazır/yazma okuma" bloğu

int make_socket_client(const ::std::string& hostname, const ::std::string& port) { 

    struct addrinfo hints; 
    struct addrinfo* res {nullptr}; 
    struct addrinfo* ptr {nullptr}; 

    memset(&hints, 0, sizeof(struct addrinfo)); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_protocol = IPPROTO_TCP; 

    int rv; 
    int connector; 

    if((rv = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res)) != 0) { 
    return -1; 
    } 

    // Try to get the first available client connection. 
    for(ptr = res; ptr != nullptr; ptr = ptr->ai_next) { 

    // Ignore undefined ip type. 
    if(ptr->ai_family != AF_INET && ptr->ai_family != AF_INET6) { 
     continue; 
    } 

    // Create a listener socket and bind it to the localhost as the server. 
    if((connector = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol)) == -1){ 
     continue; 
    } 

    make_fd_nonblocking(connector); 

    if(connect(connector, (struct sockaddr*)ptr->ai_addr, ptr->ai_addrlen) < 0) { 
     // This is what we expect. 
     if(errno == EINPROGRESS) { 
     break; 
     } 
     else { 
     close(connector); 
     continue; 
     } 
    } 
    else { 
     break; 
    } 
    } 

    freeaddrinfo(res); 

    if(ptr == nullptr) { 
    return -1; 
    } 

    return connector; 
} 
+0

Şüphesiz 'connect_fd' üzerindeki SO_ERROR'a bakıyor olmalısınız? Ve 'olay' nedir? Bu kod bir soruyu cevaplamak için yeterli değildir. – EJP

+0

Üzgünüm. sadece connect_fd olmalıdır. – Jes

+0

Orijinal 'connect()' sıfırlandı mı? – EJP

cevap

0

her şey, yerine cerring hatası. Bu neden olabilir?

asenkron TCP bağlamak iken

(-1 ile belirtildiği gibi/connect() çağrısından EINPROGRESS), onun hazır için yazma soket kümesinin parçası olarak() seçmek için soket geçmelidir sürüyor Böylece soket hazır olduğunda yazım için hazır olduğunda() işareti seçilir.

TCP bağlantısı başarılı olduğunda,() işareti, soketin yazmaya hazır (*) olduğunu döndürür. Bu olduğunda, iki olası sonuçtan hangisinin (başarı veya başarısızlık) meydana geldiğini anlamanız gerekir.

Eşzamansız olarak bağlanan bir soket() 'ın yazmaya hazır olarak seçildiğinde aradığım işlevdir.

// call this select() has indicated that (fd) is ready-for-write because 
// (fd)'s asynchronous-TCP connection has either succeeded or failed. 
// Returns true if the connection succeeded, false if the connection failed. 
// If this returns true, you can then continue using (fd) as a normal 
// connected/non-blocking TCP socket. If this returns false, you should 
// close(fd) because the connection failed. 
bool FinalizeAsyncConnect(int fd) 
{ 
#if defined(__FreeBSD__) || defined(BSD) 
    // Special case for FreeBSD7, for which send() doesn't do the trick 
    struct sockaddr_in junk; 
    socklen_t length = sizeof(junk); 
    memset(&junk, 0, sizeof(junk)); 
    return (getpeername(fd, (struct sockaddr *)&junk, &length) == 0); 
#else 
    // For most platforms, the code below is all we need 
    char junk; 
    return (send(fd, &junk, 0, 0L) == 0); 
#endif 
} 

(*) Yan not: Her şey Windows şeyler kendi yolunda yapmayı seven çünkü Windows altında biraz farklıdır: Windows altında başarılı bir asenkron (bağlamak) yukarıda açıklandığı gibi gösterilir, ancak isterseniz Windows altında başarısız bir eşzamansız connect() girişimi hakkında bildirim almak için, Windows "başarısız" fd_set altında "dışında" fd_set olduğu için, soketinizi "except" fd_set altında kaydetmeniz gerekir, çünkü Windows başarısız bir eşzamansız connect() iletmek için kullanacaktır.

+0

Teşekkür ederim Jeremy.Bununla birlikte, OSX sistemimde eşzamanlı olarak akran ile çağrı gönderme bana "SIGPIPE" hatası verir ve program çıkar ve "FinalizeAsyncConnect" işlevi hiçbir zaman geri dönmez. Bunu nasıl çözebilirim? – Jes

+1

SIGPIPE'yi ana bilgisayar() 'ın üst kısmında (SIGPIPE, SIG_IGN), aşağıda açıklandığı şekilde (SIGPIPE, SIG_IGN) çağırarak devre dışı bırakın: http://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly/108192 # 108192 –

İlgili konular