質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.24%

  • C

    2704questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • ネットワーク

    400questions

    ネットワークとは、複数のコンピューター間を接続する技術です。インターネットが最も主流なネットワークの形態で、TCP/IP・HTTP・DNSなどの様々なプロトコルや、ルータやサーバーなどの様々な機器の上に成り立っています。

C言語 ネットワークプログラミング selectの使い方

受付中

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 126

shogo812

score 1

C言語プログラミング初心者です.
ネットワークプログラミングでわからないところがあり,また質問させていただきました.
前回質問したときよりも少し簡単なものを作ろうと思いました.
クライアントが一つに対し,複数のサーバがつながっているネットワークモデルでデータのやり取りを行いたいと考え,プログラミングしてみたのですが,selectの使い方が悪いのか上手く動作してくれません.
プログラムの内容としては複数のサーバーが同時に容量の大きいデータ(下記のコードでは160MB)を作成し,送信.クライアント側でselectを用いて受信し受信時間を計測.といった形で書いてみたつもりなのです.
しかし,サーバー1(172.168.0.2),サーバー2(172.168.0.3)を起動した後,クライアント(172.168.0.1)を起動するとサーバー2のパケットは受信できたのですが,サーバー1で送信は完了するのですが,クライアント側ではサーバ1のデータ受信の計測時間が出力されず,強制終了と出力されてしまいます.

サーバー側

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>


typedef struct {
    double deg;
    double dist;
}point;

//ダミーのデータを詰める
void makeData(int datanum,point *data){
    int i;
    double col,val;
    for(i=0;i<datanum;i++){

        col = (double)i;
        data[i].deg = col;

        val += 0.01;
        data[i].dist = val;
    }

    printf("%f %f\n",col,val);
}


int main()
{
    FILE *fpr;
    int sock0;
    struct sockaddr_in addr;
    struct sockaddr_in client;
    int len;
    int sock;
    int yes = 1;

    const static int DATANUM = 10000000;
    int i;

    point *pbuf;
    int res;



    //ソケットOpen 
    sock0 = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(11111);
    addr.sin_addr.s_addr = INADDR_ANY;
    setsockopt(sock0, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
    bind(sock0, (struct sockaddr *)&addr, sizeof(addr));
    listen(sock0, 5);
    len = sizeof(client);
    sock = accept(sock0, (struct sockaddr *)&client, &len);


    //point型の配列をDATNUMだけ確保。その中にデータを入れる。
    pbuf = (point *)malloc(sizeof(point)*DATANUM);
    makeData(DATANUM,pbuf); 

    /* サーバからデータを送信 */
    res = send(sock,pbuf,sizeof(point)*DATANUM,0);
    printf( "%d bytes sent, %s\n",res, strerror( errno ) );

    close(sock);
    close(sock0);

    free(pbuf);

    return 0;
}

クライアント

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <time.h>


typedef struct {
    double deg;
    double dist;
}point;


typedef struct {
    int datanum; 
    point *meas; 
}frame;


void recvFrame(frame *f,int sock);
void recvFrame2(frame *f,int sock);

void recvFrame(frame *f,int sock){
    int i;
    int ret;
    int bytenum;
    int cur_pos = 0;
    char *rbuf;
    char *pbuf;
    point p1;
    struct timespec startTime, endTime;

    printf("sizeof(point)=%d\n",sizeof(point));

    bytenum = f->datanum * sizeof(point);
    printf("bytenum=%d\n",bytenum);

    f->meas = (point *)malloc(bytenum);
    rbuf = (char *)malloc(bytenum);
    pbuf = (char *)malloc(bytenum);


    clock_gettime(CLOCK_REALTIME, &startTime);

    while(bytenum > cur_pos){

        ret = recv(sock,rbuf,bytenum,0);

        memcpy(&(pbuf[cur_pos]),rbuf,ret);

        cur_pos += ret;
    }

    memcpy(f->meas,pbuf,bytenum);

    clock_gettime(CLOCK_REALTIME, &endTime);

    //printf("START  = %10ld.%09ld\n", startTime.tv_sec, startTime.tv_nsec);
    //printf(" END   = %10ld.%09ld\n", endTime.tv_sec, endTime.tv_nsec);
    printf("SOCK1 TIME   = ");
    if(endTime.tv_nsec < startTime.tv_nsec){
        printf("%10ld.%09ld", endTime.tv_sec -startTime.tv_sec - 1, endTime.tv_nsec + 1000000000 - startTime.tv_nsec);
    }else{
        printf("%10ld.%09ld",endTime.tv_sec - startTime.tv_sec, endTime.tv_nsec - startTime.tv_nsec);
    }
    printf("(sec)\n");

    free(pbuf);
    free(rbuf);
}

void recvFrame2(frame *f,int sock){
    int i;
    int ret;
    int bytenum;
    int cur_pos = 0;
    char *rbuf;
    char *pbuf;
    point p1;
    struct timespec startTime, endTime;

    printf("sizeof(point)=%d\n",sizeof(point));

    bytenum = f->datanum * sizeof(point);
    printf("bytenum=%d\n",bytenum);

    f->meas = (point *)malloc(bytenum);
    rbuf = (char *)malloc(bytenum);
    pbuf = (char *)malloc(bytenum);


    clock_gettime(CLOCK_REALTIME, &startTime);

    while(bytenum > cur_pos){

        ret = recv(sock,rbuf,bytenum,0);

        memcpy(&(pbuf[cur_pos]),rbuf,ret);

        cur_pos += ret;
    }

    memcpy(f->meas,pbuf,bytenum);

    clock_gettime(CLOCK_REALTIME, &endTime);

    //printf("START  = %10ld.%09ld\n", startTime.tv_sec, startTime.tv_nsec);
    //printf(" END   = %10ld.%09ld\n", endTime.tv_sec, endTime.tv_nsec);
    printf("SOCK2 TIME   = ");
    if(endTime.tv_nsec < startTime.tv_nsec){
        printf("%10ld.%09ld", endTime.tv_sec -startTime.tv_sec - 1, endTime.tv_nsec + 1000000000 - startTime.tv_nsec);
    }else{
        printf("%10ld.%09ld",endTime.tv_sec - startTime.tv_sec, endTime.tv_nsec - startTime.tv_nsec);
    }
    printf("(sec)\n");

    free(pbuf);
    free(rbuf);
}


int main()
{
    struct sockaddr_in server, server2;
    int sock,sock2;
    fd_set fds, readfds;
    int maxfd;
    int len, len2;
    int n;
    struct timeval tv;

    sock  = socket(AF_INET, SOCK_STREAM, 0);
    sock2 = socket(AF_INET, SOCK_STREAM, 0);

    server.sin_family = AF_INET;
    server.sin_port = htons(11111);
    server.sin_addr.s_addr = inet_addr("172.168.0.2");

    server2.sin_family = AF_INET;
    server2.sin_port = htons(22222);
    server2.sin_addr.s_addr = inet_addr("172.168.0.3");

    connect(sock, (struct sockaddr *)&server, sizeof(server));
    connect(sock2, (struct sockaddr *)&server2, sizeof(server2));

    FD_ZERO(&readfds);

    FD_SET(sock, &readfds);
    FD_SET(sock2, &readfds);

    tv.tv_sec = 10;
    tv.tv_usec = 0;

    if(sock > sock2){
        maxfd = sock;
    }else{
        maxfd = sock2;
    }

    frame f1,f2;
    f1.datanum = 10000000;
    f2.datanum = 10000000;

    while (1) {
        memcpy(&fds, &readfds, sizeof(fd_set));

        n = select(maxfd+1, &fds, NULL, NULL, &tv);

        if(n==0){
            printf("timeout\n");
            break;
        }

        if(FD_ISSET(sock, &fds)){
                recvFrame(&f1,sock);
        }

        if(FD_ISSET(sock2, &fds)){
                recvFrame2(&f2,sock2);
        }

    }

    close(sock);
    close(sock2);

    return 0;
}

selectの使い方がまずいのでしょうか?
どなたかお力添えよろしくお願いします.

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

0

サーバーのコードを見ると送信が完了したらソケットをクローズしています。

fd_setの領域にはあくまで「通信中のファイルディスクリプターのみ」を設定すべきですが、ご質問のコードではそれを気にせずサーバー側がクローズ済みかも知れないファイルディスクリプターも含めて毎回readfdsに指定してしまってますね?

通信相手がソケットをクローズした場合もselectはそれを検知できます(EOFとして)。そのソケットでの通信が完了したことを検知したら速やかにcloseし、以降のselectではそのファイルディスクリプターをselectの対象外にすべきでしょう。そうしないとselectは期待通りの動作(監視対象のソケットがreadyになるまでプロセスをブロック)になってくれないと思います。


writefdsを省略する(NULL)のはよいですけど、エラーを検知できた方がより精密な処理が作れる気がするのでerrorfdsは省略しない方がよいかも知れません。エラーが起きた時それを検出しないでいるとselectはどうなるんでしょうか・・・-1が返ってくるからエラーが起きたことはわかるのでしょうが、「どのソケットで異常が発生したか」などは処理を書きにくい気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

これは「1クライアント」が「複数のサーバー」に接続することを意図したプログラムのように思えます。そういうシステム設計が存在しないわけではありませんが、変則的ではあります。初心者ということならば、まずは一般的な形から試してみることなんじゃないでしょうか。select()の使い所を学ぶためであれば1対1通信でもできます。その場合でも、select()による待ち処理を持たせるのはサーバー側のプログラムでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.24%

関連した質問

同じタグがついた質問を見る

  • C

    2704questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • ネットワーク

    400questions

    ネットワークとは、複数のコンピューター間を接続する技術です。インターネットが最も主流なネットワークの形態で、TCP/IP・HTTP・DNSなどの様々なプロトコルや、ルータやサーバーなどの様々な機器の上に成り立っています。