Armadilloフォーラム

socketの挙動について

kes-konishi

2021年2月9日 9時20分

KES)小西です。

開発するにあたって、気を付けた方がいい情報を展開いたします。

Armadillo G3M等での確認結果になります。
socketを使用して、クライアントとしてTCPのコネクションをノンブロッキングIOで処理する場合、
通常は以下のようなコードで接続すると思われますが、接続先がいない場合、
この挙動が変わるので注意が必要です。

サンプルコードとしてなので、エラー系等は割愛します。

    SOCKET_TYPE s;
    struct sockaddr_in server;
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        //error
        goto l_err;
    }
    fcntl(s, F_SETFL, O_NONBLOCK);      //nonblocking
    server.sin_addr.s_addr = inet_addr(server_ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    (void)connect(s, (struct sockaddr *)&server, sizeof(server));   //non blocking socket always returns -1
 
    FD_ZERO(&writefds);
    FD_SET(s, &writefds);
    FD_ZERO(&readfds);
    FD_SET(s, &readfds);
    while (1) {
        FD_ZERO(&exfds);
        memcpy(&rfds, &readfds, sizeof(fd_set));
        memcpy(&wfds, &writefds, sizeof(fd_set));
        ret = pselect(s + 1, &rfds, &wfds, &exfds, &remain_timeout, NULL);
        error_no = errno;
        if (ret < 0) {              //error
             switch (error_no) {
             case EINTR:
                 continue;
             default:
                 goto l_err;
        } else if (ret == 0) {      //timeout
            goto l_err;
        } else {
            if (FD_ISSET(s, &wfds)) {                    //connected
                //一般的な接続判定!
            }
        }

上記のselectでwriteイベントありでコネクションが張れたと判断するのが一般的でしたが、
コネクションが張れてなくても、このwriteイベントありでselectが抜けることがあります。
この状態でnetstatで見ても、コネクションが張れておらず(SYN_SENT、CONECTED等も無い)
write or readして初めてENOTCONN、EHOSTUNREACHで認識するようです。
この現象はat13以降で確認しており、
・at13の場合
 起動時はconnectでのselectはタイムアウトします。
 mmcliでifdown→ifupした後、selectはタイムアウトせず(タイムアウト時間まで待ったあと)writeイベントありで抜けてきます。
 →その後放置しておくと、起動時と同様にタイムアウト動作になることがありました。
・at17の場合
 起動時はselectはタイムアウトせず(タイムアウト時間まで待ったあと)writeイベントありで抜けてきます。
 ifdown→ifupしても同様の動作となります。

本現象はArmadillo固有の動作かまだ判断はできておりません。
また、
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/connect.2.html
でのEINPROGRESSの解説の挙動と変わっています。
また、発生したときにgetsockoptで SOL_SOCKET レベル、SO_ERROR オプション で読みだしても正常で返ってきました。

ただし、上記が発生しているときは、readのイベントもselectで登録することで、
コネクションが張れた場合はwriteイベントのみイベントありとなり、
コネクションが張れていない場合はwrite、readイベント両方がイベントありとなるようです。

このため、上記の状態でread側イベントを判断して、エラー検出するしか回避方法が無いようです。

OSSのパッケージでエラー処理が弱いものは影響するかもしれません。

アットマーク様
本現象はどのバージョンから変更になっているかわかりますでしょうか?