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

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

ただいまの
回答率

90.45%

  • C

    4679questions

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

  • Linux

    4535questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

  • CentOS

    3218questions

    CentOSは、主にRed Hat Enterprise Linux(RHEL)をベースにした、フリーのソフトウェアオペレーティングシステムです。

strtokの使い方およびファイルオープンエラーの解決方法

受付中

回答 1

投稿 編集

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

SioRyu

score 5

大学の授業で習っている最中なのですが、通信システムのシミュレーションをしたいプログラムで。出された課題内容がプログラムの機能を拡張するもので。一つ目の課題は特に問題なくクリアしたのですが二つ目でエラーが出てしまいどう解決すればいいかわからず、皆様に直し方をお聞きしたいです。
以前でも同じような課題が出てその課題と類似しています。
strtokでファイル名を","で区切り二つのファイル名を入力後、その二つのファイルの中に入っているものを出力するプログラムを書きたいのですがFile access errorになってしまいます。
strtokの使い方が間違っているのでしょうか?
strtokでjp.txtとch.txtに分けられておらず中身のbufもそれぞれのbut[0]とbuf[1]に格納できていないのでしょうか?

動作環境は大学のパソコンで
仮想マシンCentOS Linux socket通信環境

  1. サーバー側
/* コネクション型の簡単なリモートファイル表示サーバ(vc_server.c) */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h> /* ソケットのための基本的なヘッダファイル      */
#include <netinet/in.h> /* インタネットドメインのためのヘッダファイル  */
#include <netdb.h>      /* gethostbyname()を用いるためのヘッダファイル */
#include <errno.h>
#include <string.h>
#define  MAXHOSTNAME    64
#define  S_TCP_PORT    (u_short)5000  /* 本サーバが用いるポート番号 */
#define  MAXFILENAME    255
#define  MAXBUFLEN    512
#define  ERR        0 /* ファイルオープン失敗 */
#define     OK        1 /*                 成功 */
int setup_vcserver(struct hostent*, u_short);
void send_file(int);

main()
{
    int    socd, socd1;
    char    s_hostname[MAXHOSTNAME];
    struct hostent    *s_hostent;
    struct sockaddr_in    c_address;
    int    c_addrlen, cpid;

    /* サーバのホスト名とそのIPアドレス(をメンバに持つhostent構造体)を求める */
    gethostname(s_hostname, sizeof(s_hostname));
    s_hostent = gethostbyname(s_hostname);

    /* バーチャルサーキットサーバの初期設定 */
    socd = setup_vcserver(s_hostent, S_TCP_PORT);

    while(1) {
        /* 接続要求の受け入れ */
        c_addrlen = sizeof(c_address);
        if((socd1 = accept(socd, (struct sockaddr *)&c_address, &c_addrlen)) < 0) {
            perror("accept");
            exit(1);
        }
        /* フォーク(並行サーバのサービス) */
        if((cpid = fork()) < 0) { perror("fork");exit(1); }
        else if(cpid == 0) { /* 子プロセス */
            close(socd);

            /* クライアントが要求するファイルの送信 */
            send_file(socd1);

            close(socd1);
            exit(0);
        }
        else close(socd1);   /* 親プロセス */
    }
}

int setup_vcserver(struct hostent *hostent, u_short port)
{
    int    socd;
    struct sockaddr_in    s_address;

    /* インターネットドメインのSOCK_STREAM(TCP)型ソケットの構築 */
    if((socd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");exit(1); }

    /* アドレス(IPアドレスとポート番号)の作成 */
    bzero((char *)&s_address, sizeof(s_address));
    s_address.sin_family = AF_INET;
    s_address.sin_port = htons(port);
    bcopy((char *)hostent->h_addr, (char *)&s_address.sin_addr, hostent->h_length);

    /* アドレスのソケットへの割り当て */
    if(bind(socd, (struct sockaddr *)&s_address, sizeof(s_address)) < 0) { perror("bind");exit(1); }

    /* 接続要求待ち行列の長さを5とする */
    if(listen(socd, 5) < 0) { perror("listen");exit(1); }

    return socd;
}

void send_file(int socd) /* クライアントが要求するファイルを読み込みソケットに書き出す */
{
    char    filename[MAXFILENAME+1];
    FILE    *fd;
    char    ack;
    char    buf[i][MAXBUFLEN];
    int i=0;
    char token;
    char    **dbp
    for(;;)
    {
        /* クライアントから送られるファイル名をソケットから読み込む */
        recv(socd, filename, MAXFILENAME+1, 0);
        token = *strtok(filename, ",");
        do{
          while(*dbp){
            if(strcmp(&token, *dbp) == 0) {
            strcpy(buf[i], *(++dbp));
            break;
            }
            dbp += 1;
         }
         i++;
        }while(token == *strtok(NULL,","));
        if(*dbp == NULL) strcpy(*data, "No entry");
        /* ファイルを読み出し専用にオープンする */
        if((fd = fopen(filename, "r")) != NULL) { /* ファイルオープンに成功した場合 */
            /* オープン成功メッセージを送る */
            ack = OK;
            send(socd, &ack, 1, 0);
            /* ファイルから1行読み込みソケットに書き出すことをEOFを読むまで繰り返す */
            printf("ファイル %s を送信\n",filename);
            while(fgets(buf[i], MAXBUFLEN, fd)) {
                send(socd, buf[i], strlen(buf[i]), 0);
            }
            close(fd);
        }
        else 
        {                                    /* ファイルオープンに失敗した場合 */
            /* オープン失敗メッセージを送る */
            ack = ERR;
            send(socd, &ack, 1, 0);
        }
    }
}
  1. クライアント側
/* コネクション型の簡単なリモートファイル表示クライアント(vc_client.c) */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h> /* ソケットのための基本的なヘッダファイル      */
#include <netinet/in.h> /* インタネットドメインのためのヘッダファイル  */
#include <netdb.h>      /* gethostbyname()を用いるためのヘッダファイル */
#include <errno.h>
#include <string.h>
#define  MAXHOSTNAME    64
#define     S_TCP_PORT    (u_short)5000 
#define  MAXFILENAME    255
#define     MAXBUFLEN    512  
#define     ERR        0 /* ファイルオープン失敗 */
#define  OK        1 /*                 成功 */
int setup_vcclient(struct hostent*, u_short);
void receive_file(int);

main()
{
    int    socd;
    char    s_hostname[MAXHOSTNAME];
    struct hostent    *s_hostent;

    /* サーバのホスト名の入力 */
    printf("server host name?: "); scanf("%s",s_hostname);
    /* サーバホストのIPアドレス(をメンバに持つhostent構造体)を求める */
    if((s_hostent = gethostbyname(s_hostname)) == NULL) {
        fprintf(stderr, "server host does not exists\n");
        exit(1);
    }

    /* バーチャルサーキットクライアントの初期設定 */
    socd = setup_vcclient(s_hostent, S_TCP_PORT);

    /* サーバにファイルを要求し受信したファイルの内容を標準出力に出力 */
    receive_file(socd);

    close(socd);
    exit(0);
}

int setup_vcclient(struct hostent *hostent, u_short port)
{
        int     socd;
        struct sockaddr_in      s_address;

    /* インターネットドメインのSOCK_STREAM(TCP)型ソケットの構築 */
        if((socd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");exit(1); }

    /* サーバのアドレス(IPアドレスとポート番号)の作成 */
        bzero((char *)&s_address, sizeof(s_address));
        s_address.sin_family = AF_INET;
        s_address.sin_port = htons(port);
        bcopy((char *)hostent->h_addr, (char *)&s_address.sin_addr, hostent->h_length);

    /* サーバとの接続の確立 */
    if(connect(socd, (struct sockaddr *)&s_address, sizeof(s_address)) < 0) { perror("connect");exit(1); }

    return socd;
}

void receive_file(int socd) /* サーバから受け取ったファイルの内容を表示する */
{
    char    filename[MAXFILENAME+1];
    int    filename_len;
    char    ack;
    char    buf[i][MAXBUFLEN];
    int    length;
    int i;

    for(;;)
    {
        /* ファイル名の入力 */
        printf("remote file name?: ");
        if(scanf("%s",filename)==EOF)break;
        /* ファイル名をソケットに書き込む */
        filename_len = strlen(filename);
        send(socd, filename, filename_len+1, 0);
        /* ファイルオープンに成功したかどうかのメッセージをソケットから読み込む */
        recv(socd, &ack, 1, 0);
        switch (ack) {
        case OK: /* ファイルオープンに成功した場合 */
            printf("ファイル %s を受信\n", filename);
            /* ソケットから読み込み標準出力に書き出す */
            while(length = recv(socd, buf[i], MAXBUFLEN, 0)) {
                buf[i][length] = '\0';
                for(i=0;i<5;i++){

                  fputs(buf[i], stdout);
                }
                break;
            }
            printf("\n");
            break;
        case ERR: /* ファイルオープンに失敗した場合 */
            fprintf(stderr, "File access error\n");
            break;
        }
    }
}

実行結果:
[root@skt35 Linux_share]# gcc -o client client.c
[root@skt35 Linux_share]# ./client
server host name?: skt35
remote file name?: jp.txt,ch.txt
File access error
[root@skt35 Linux_share]# gcc -o server server.c
[root@skt35 Linux_share]# ./server
//何も表示されない

サーバー側(第一課題を実行できていたもの)

void send_file(int socd) /* クライアントが要求するファイルを読み込みソケットに書き出す */
{
    char    filename[MAXFILENAME+1];
    FILE    *fd;
    char    ack;
    char    buf[MAXBUFLEN];
    for(;;)
    {
        /* クライアントから送られるファイル名をソケットから読み込む */
        recv(socd, filename, MAXFILENAME+1, 0);
        /* ファイルを読み出し専用にオープンする */
        if((fd = fopen(filename, "r")) != NULL) { /* ファイルオープンに成功した場合 */
            /* オープン成功メッセージを送る */
            ack = OK;
            send(socd, &ack, 1, 0);
            /* ファイルから1行読み込みソケットに書き出すことをEOFを読むまで繰り返す */
            printf("ファイル %s を送信\n",filename);
            while(fgets(buf, MAXBUFLEN, fd)) {
                send(socd, buf, strlen(buf), 0);
            }
            close(fd);
            printf("\n");
        }
        else 
        {                                    /* ファイルオープンに失敗した場合 */
            /* オープン失敗メッセージを送る */
            ack = ERR;
            send(socd, &ack, 1, 0);
        }
    }
}

クライアント側(第一課題を実行できたもの)

void receive_file(int socd) /* サーバから受け取ったファイルの内容を表示する */
{
    char    filename[MAXFILENAME+1];
    int    filename_len;
    char    ack;
    char    buf[MAXBUFLEN];
    int    length;
    for(;;)
    {

        /* ファイル名の入力 */
        printf("remote file name?: ");
        printf("what");
        if(scanf("%s",filename)==EOF)break;
        printf("at");
        /* ファイル名をソケットに書き込む */
        filename_len = strlen(filename);
        printf("that");
        send(socd, filename, filename_len+1, 0);
        printf("chat");
        /* ファイルオープンに成功したかどうかのメッセージをソケットから読み込む */
        recv(socd, &ack, 1, 0);
        printf("hat");
        switch (ack) {
        case OK: /* ファイルオープンに成功した場合 */
            printf("ファイル %s を受信\n", filename);
            /* ソケットから読み込み標準出力に書き出す */
            while(length = recv(socd, buf, MAXBUFLEN, 0)) {
                buf[length] = '\0';
                fputs(buf, stdout);
                break;
            }
            break;
        case ERR: /* ファイルオープンに失敗した場合 */
            fprintf(stderr, "File access error\n");
            break;
        }
    }
}


サーバー側とクライアント側ともに最後の関数しか変更しておらず、この部分のみの状態でしたら実行できました。
今回に関しては複数の入力に対して複数の出力ができればいいので。特に","で区切らないといけないという訳でもなく、複数という条件しか出されていなかったので最小二つに対応できればいいです。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • tatsu99

    2018/10/25 22:32

    提示されたソースをコンパイルするとserver.c client.c共に、コンパイルエラーとなります。エラーのとれたソースを提示していただけませんでしょうか。

    キャンセル

  • tatsu99

    2018/10/25 23:55

    クライアントから送られるファイル名は、「必ず、カンマ区切りで2つである」という前提でよいのですか。それとも1つの時とか、3つ以上のケースも考慮する必要があるのですか。

    キャンセル

  • SioRyu

    2018/10/26 01:33

    夜遅くにありがとうございます、先日はとても助かりました。明日もう少し自分で粘ってみたいと思います。tiitoiさんに教わったことを明日調べながら試して、それでもできなかったらまた参考にさせていただきます。

    キャンセル

回答 1

0

プログラム自体は動かせていませんが、文字列分割の部分だけコメントします。

strtok() の仕様は少々複雑です。

char *strtok(char *s1, const char *s2);

最初の呼び出しでは s1 には分解対象の文字列を指定します。トークンがあれば、strtok() はトークンへのポインタを返却します。
2回目以降の呼び出しでは s1 に NULL を指定します。分解できるトークンがあるうちは、strtok() はトークンへのポインタを返却します。
トークンがなくなると strtok() は NULL を返却します。
strtok() はトークンへのポインタを返却しながら、分解対象文字列 s1 中の区切り文字に空文字('\0')を埋めていきます。ですから、s1 に文字列リテラルや変更されては困る文字列を指定してはいけません。
引用元

なので、以下のようにするべきではないでしょうか?

#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "file1.txt,file2.txt,file3.txt";

    char *filename = strtok(str, ",");
    // 1回目の呼び出しでは、トークン、つまり先頭のポインタを返す。
    // 見つけた区切り文字は\0で置き換えられるので、filename = "file1.txt" となっている。

    do {
        printf("%s\n", filename);
    // 2回目以降の呼び出しのときは、第一引数は NULL を指定する。
    // filename = "file2.txt" となっている。
    // その次の呼び出した場合、
    // filename = "file3.txt" となっている。
    // その次の呼び出した場合、もうトークンが残ってないので NULL を返す。
    } while (filename = strtok(NULL, ","));
}
file1.txt
file2.txt
file3.txt

記載のコードですと、最後の呼び出しの際に *NULL となり、null pointer に対する de-reference 操作となり、C言語では、動作未定義なので危険です。

token = *strtok(filename, ",");
do {
    // 処理
} while (token == *strtok(NULL, ","));

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

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

  • C

    4679questions

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

  • Linux

    4535questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

  • CentOS

    3218questions

    CentOSは、主にRed Hat Enterprise Linux(RHEL)をベースにした、フリーのソフトウェアオペレーティングシステムです。