2016-04-14 9 views
0

私は接続のためにタイムアウトを実装する必要があるソケットクライアントを構築しています。ソケット接続を切り離されたスレッドから中止する

すべてのトランザクションで開始され、トランザクション完了時にキャンセルされる分離されたスレッドで単純なタイマーを使用することを考えています。この同じ手法は、異なるタイムアウトを使用するプロトコル制御に使用されます。タイマーのソケットがエラーで中止することを強制的に、「接続」操作をキャンセルされていません閉会

#include <string> 
#include <sstream> 
#include <map> 
#include <iostream> 
#include <cstring> 
#include <thread> 

#ifdef _WIN32 
#include <io.h> 
#include <winsock2.h> 
#include <ws2tcpip.h> 
#include <Windows.h> 
#else 
#include <unistd.h> 
#include <sys/socket.h> 
#include <netdb.h> 
#include <sys/types.h> 
#endif 

#include <stdio.h> 
#include <stdlib.h> 

bool timerOn = false; 
int currentSocket = 0; 


void Timer(int seconds) 
{ 
    int tick = seconds; 

    while (tick > 0) 
    { 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
     tick--; 
    } 

    if (timerOn) 
     close(currentSocket); 
} 

void StartTimer(int seconds) 
{ 
    timerOn = true; 
    std::thread t(&Timer, seconds); 
    t.detach(); 
} 

void StopTimer() 
{ 
    timerOn = false; 
} 


void Connect(std::string address, int port) 
{ 
    struct addrinfo hints; 
    struct addrinfo *result = NULL; 
    struct addrinfo *rp = NULL; 
    int sfd, s; 

    std::memset(&hints, 0, sizeof(struct addrinfo)); 

    hints.ai_family = AF_UNSPEC;  /* Allow IPV4 or IPV6 */ 
    hints.ai_socktype = SOCK_STREAM;  
    hints.ai_flags = 0; 
    hints.ai_protocol = 0;    

    std::string portStr; 
    portStr = std::to_string(port); 

    s = getaddrinfo(address.c_str(), portStr.c_str(), &hints, &result); 

    if (s != 0) 
    { 
     std::stringstream ss; 
     ss << "Cannot resolve hostname " << address << gai_strerror(s); 
     throw std::runtime_error(ss.str()); 
    } 

    for (rp = result; rp != NULL; rp = rp->ai_next) 
    { 
     sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 

     if (sfd == -1) 
      continue; 

     StartTimer(10); 
     int sts = connect(sfd, rp->ai_addr, rp->ai_addrlen); 
     StopTimer(); 

     if (sts == 0) 
      break; 

     close(sfd); 
    } 

    freeaddrinfo(result); /* Object no longer needed */ 

    if (rp == NULL) 
    { 
     std::stringstream ss; 
     ss << "Cannot find server address at " << address << " port " << port; 
     throw std::runtime_error(ss.str()); 
    } 

    currentSocket = sfd; 
} 


int main() 
{ 
    try 
    { 
     Connect("192.168.0.187", 9090); 
     std::cout << "Connected to server. Congrats!!!" << std::endl; 
    } 
    catch (std::exception& ex) 
    { 
     std::cout << "Error connecting to server. Aborting." << std::endl; 
     std::cout << ex.what() << std::endl; 
    } 
} 

:テストへ

は、私は次の簡単なコードをやっています。私もshutdown(sfd, SHUT_RDWR);を試しました...

私のアプローチは無効ですか?それはなぜ機能しないのですか?

connectを分離スレッドからエラーで中止する方法を教えてください。

+0

すべてのプラットフォームで、単にソケットを閉じるだけで 'connect()'が中断されるわけではありません。 –

+0

@RemyLebeau私はそれが何も知らない。それは時々運が起こるかもしれませんが、文字通り保証することは不可能です。(通常の 'socket'、' connect'、 'close' APIだけでは、文字通り実行できません。アトミックな"接続してからロックを解除する "機能などの追加機能を持つAPIが必要です)。 –

+0

@DavidSchwartz Windowsソケットを閉じることによって 'connect()'が打ち切られるようにします。 –

答えて

4

タイマーでソケットを閉じると、「接続」操作がキャンセルされ、強制的にエラーが発生します。

Whoa!あなたは絶対にそれをすることはできません。ソケットを閉じるときにスレッドが実際にconnectにブロックされていることを知る方法はありません(connectを呼び出すことではなく)。あるスレッドが別のスレッドを使用している間にリソースを解放することは、そのリソースを使用することは災害のレシピです。

は、この問題が発生した想像:

  1. スレッドはconnectを呼び出すしようとしているので、それがタイムアウトするための準備をします。

  2. タイムアウトが経過し、ソケットが閉じられます。

  3. いくつかのライブラリのスレッドは、同じソケット記述子を取得するために、何らかの理由で使用するために新しいソケットを作成します。

  4. 最終的にconnectを呼び出すスレッドがスケジュールされ、connect - ライブラリのソケットを接続しています!災害。

次の2つの選択肢があります。

  1. 非ブロックconnect操作を使用します。

  2. connectを呼び出すスレッドを中断する信号を使用します。

しかし、私はあなたがなぜ気にしているのですか?なぜあなたは接続を中止する必要がありますか?タイムアウトの前に接続が成功しなかった場合は、何か他のことをする必要がある場合は、先に進んでください。

+0

"なぜあなたは気にしませんか?" - タイムアウトがなければ、 'connect()'はエラーを出して終了するのに時間がかかるかもしれません。宛先に到達するために必要なホップ数に応じて、一方向またはそれ以外のものを発見するにはしばらく時間がかかることがあります。現代のネットワークと同じくらい早く、彼らはまだ瞬時ではありません。タイムアウト後に中止しなければ、それは後で戻ってくるかもしれないし、「ああ、私はついにそれを作った」と言うかもしれませんが、あなたはすでに動いています。 –

+0

@RemyLebeauだから何?それはなぜ問題なのですか?たとえあなたがそれを打ち切っても、それはまだ成功する可能性があります - 実際には成功しているかもしれません、あなたはちょうどそのような最終的なパケットを受け取っていません(あるいは相手側はそれをまだ受け取っていませんが、受信した応答を送信して接続試行を完了します)。だからすでに成功しているかもしれない(あるいは後で成功するかもしれない)接続を中止する可能性について何もできません。 –

+0

下位レベルで成功しても上位レベルのソケットは中止/閉鎖されるため、後続のトラフィックはリセット応答で失敗するはずです。 –

関連する問題