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

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

新規登録して質問してみよう
ただいま回答率
85.51%
CentOS

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

C

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

Linux

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

Q&A

解決済

1回答

3459閲覧

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

SioRyu

総合スコア28

CentOS

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

C

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

Linux

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

0グッド

1クリップ

投稿2018/10/25 09:20

編集2018/10/25 16:27

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

動作環境は大学のパソコンで
仮想マシンCentOS Linux socket通信環境
0. サーバー側

c

1/* コネクション型の簡単なリモートファイル表示サーバ(vc_server.c) */ 2#include <stdio.h> 3#include <stdlib.h> 4#include <sys/types.h> 5#include <sys/socket.h> /* ソケットのための基本的なヘッダファイル */ 6#include <netinet/in.h> /* インタネットドメインのためのヘッダファイル */ 7#include <netdb.h> /* gethostbyname()を用いるためのヘッダファイル */ 8#include <errno.h> 9#include <string.h> 10#define MAXHOSTNAME 64 11#define S_TCP_PORT (u_short)5000 /* 本サーバが用いるポート番号 */ 12#define MAXFILENAME 255 13#define MAXBUFLEN 512 14#define ERR 0 /* ファイルオープン失敗 */ 15#define OK 1 /* 成功 */ 16int setup_vcserver(struct hostent*, u_short); 17void send_file(int); 18 19main() 20{ 21 int socd, socd1; 22 char s_hostname[MAXHOSTNAME]; 23 struct hostent *s_hostent; 24 struct sockaddr_in c_address; 25 int c_addrlen, cpid; 26 27 /* サーバのホスト名とそのIPアドレス(をメンバに持つhostent構造体)を求める */ 28 gethostname(s_hostname, sizeof(s_hostname)); 29 s_hostent = gethostbyname(s_hostname); 30 31 /* バーチャルサーキットサーバの初期設定 */ 32 socd = setup_vcserver(s_hostent, S_TCP_PORT); 33 34 while(1) { 35 /* 接続要求の受け入れ */ 36 c_addrlen = sizeof(c_address); 37 if((socd1 = accept(socd, (struct sockaddr *)&c_address, &c_addrlen)) < 0) { 38 perror("accept"); 39 exit(1); 40 } 41 /* フォーク(並行サーバのサービス) */ 42 if((cpid = fork()) < 0) { perror("fork");exit(1); } 43 else if(cpid == 0) { /* 子プロセス */ 44 close(socd); 45 46 /* クライアントが要求するファイルの送信 */ 47 send_file(socd1); 48 49 close(socd1); 50 exit(0); 51 } 52 else close(socd1); /* 親プロセス */ 53 } 54} 55 56int setup_vcserver(struct hostent *hostent, u_short port) 57{ 58 int socd; 59 struct sockaddr_in s_address; 60 61 /* インターネットドメインのSOCK_STREAM(TCP)型ソケットの構築 */ 62 if((socd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");exit(1); } 63 64 /* アドレス(IPアドレスとポート番号)の作成 */ 65 bzero((char *)&s_address, sizeof(s_address)); 66 s_address.sin_family = AF_INET; 67 s_address.sin_port = htons(port); 68 bcopy((char *)hostent->h_addr, (char *)&s_address.sin_addr, hostent->h_length); 69 70 /* アドレスのソケットへの割り当て */ 71 if(bind(socd, (struct sockaddr *)&s_address, sizeof(s_address)) < 0) { perror("bind");exit(1); } 72 73 /* 接続要求待ち行列の長さを5とする */ 74 if(listen(socd, 5) < 0) { perror("listen");exit(1); } 75 76 return socd; 77} 78 79void send_file(int socd) /* クライアントが要求するファイルを読み込みソケットに書き出す */ 80{ 81 char filename[MAXFILENAME+1]; 82 FILE *fd; 83 char ack; 84 char buf[i][MAXBUFLEN]; 85 int i=0; 86 char token; 87 char **dbp 88 for(;;) 89 { 90 /* クライアントから送られるファイル名をソケットから読み込む */ 91 recv(socd, filename, MAXFILENAME+1, 0); 92 token = *strtok(filename, ","); 93 do{ 94 while(*dbp){ 95 if(strcmp(&token, *dbp) == 0) { 96 strcpy(buf[i], *(++dbp)); 97 break; 98 } 99 dbp += 1; 100 } 101 i++; 102 }while(token == *strtok(NULL,",")); 103 if(*dbp == NULL) strcpy(*data, "No entry"); 104 /* ファイルを読み出し専用にオープンする */ 105 if((fd = fopen(filename, "r")) != NULL) { /* ファイルオープンに成功した場合 */ 106 /* オープン成功メッセージを送る */ 107 ack = OK; 108 send(socd, &ack, 1, 0); 109 /* ファイルから1行読み込みソケットに書き出すことをEOFを読むまで繰り返す */ 110 printf("ファイル %s を送信\n",filename); 111 while(fgets(buf[i], MAXBUFLEN, fd)) { 112 send(socd, buf[i], strlen(buf[i]), 0); 113 } 114 close(fd); 115 } 116 else 117 { /* ファイルオープンに失敗した場合 */ 118 /* オープン失敗メッセージを送る */ 119 ack = ERR; 120 send(socd, &ack, 1, 0); 121 } 122 } 123} 124
  1. クライアント側

c

1/* コネクション型の簡単なリモートファイル表示クライアント(vc_client.c) */ 2#include <stdio.h> 3#include <stdlib.h> 4#include <sys/types.h> 5#include <sys/socket.h> /* ソケットのための基本的なヘッダファイル */ 6#include <netinet/in.h> /* インタネットドメインのためのヘッダファイル */ 7#include <netdb.h> /* gethostbyname()を用いるためのヘッダファイル */ 8#include <errno.h> 9#include <string.h> 10#define MAXHOSTNAME 64 11#define S_TCP_PORT (u_short)5000 12#define MAXFILENAME 255 13#define MAXBUFLEN 512 14#define ERR 0 /* ファイルオープン失敗 */ 15#define OK 1 /* 成功 */ 16int setup_vcclient(struct hostent*, u_short); 17void receive_file(int); 18 19main() 20{ 21 int socd; 22 char s_hostname[MAXHOSTNAME]; 23 struct hostent *s_hostent; 24 25 /* サーバのホスト名の入力 */ 26 printf("server host name?: "); scanf("%s",s_hostname); 27 /* サーバホストのIPアドレス(をメンバに持つhostent構造体)を求める */ 28 if((s_hostent = gethostbyname(s_hostname)) == NULL) { 29 fprintf(stderr, "server host does not exists\n"); 30 exit(1); 31 } 32 33 /* バーチャルサーキットクライアントの初期設定 */ 34 socd = setup_vcclient(s_hostent, S_TCP_PORT); 35 36 /* サーバにファイルを要求し受信したファイルの内容を標準出力に出力 */ 37 receive_file(socd); 38 39 close(socd); 40 exit(0); 41} 42 43int setup_vcclient(struct hostent *hostent, u_short port) 44{ 45 int socd; 46 struct sockaddr_in s_address; 47 48 /* インターネットドメインのSOCK_STREAM(TCP)型ソケットの構築 */ 49 if((socd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");exit(1); } 50 51 /* サーバのアドレス(IPアドレスとポート番号)の作成 */ 52 bzero((char *)&s_address, sizeof(s_address)); 53 s_address.sin_family = AF_INET; 54 s_address.sin_port = htons(port); 55 bcopy((char *)hostent->h_addr, (char *)&s_address.sin_addr, hostent->h_length); 56 57 /* サーバとの接続の確立 */ 58 if(connect(socd, (struct sockaddr *)&s_address, sizeof(s_address)) < 0) { perror("connect");exit(1); } 59 60 return socd; 61} 62 63void receive_file(int socd) /* サーバから受け取ったファイルの内容を表示する */ 64{ 65 char filename[MAXFILENAME+1]; 66 int filename_len; 67 char ack; 68 char buf[i][MAXBUFLEN]; 69 int length; 70 int i; 71 72 for(;;) 73 { 74 /* ファイル名の入力 */ 75 printf("remote file name?: "); 76 if(scanf("%s",filename)==EOF)break; 77 /* ファイル名をソケットに書き込む */ 78 filename_len = strlen(filename); 79 send(socd, filename, filename_len+1, 0); 80 /* ファイルオープンに成功したかどうかのメッセージをソケットから読み込む */ 81 recv(socd, &ack, 1, 0); 82 switch (ack) { 83 case OK: /* ファイルオープンに成功した場合 */ 84 printf("ファイル %s を受信\n", filename); 85 /* ソケットから読み込み標準出力に書き出す */ 86 while(length = recv(socd, buf[i], MAXBUFLEN, 0)) { 87 buf[i][length] = '\0'; 88 for(i=0;i<5;i++){ 89 90 fputs(buf[i], stdout); 91 } 92 break; 93 } 94 printf("\n"); 95 break; 96 case ERR: /* ファイルオープンに失敗した場合 */ 97 fprintf(stderr, "File access error\n"); 98 break; 99 } 100 } 101} 102

実行結果:

[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
//何も表示されない

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

c

1void send_file(int socd) /* クライアントが要求するファイルを読み込みソケットに書き出す */ 2{ 3 char filename[MAXFILENAME+1]; 4 FILE *fd; 5 char ack; 6 char buf[MAXBUFLEN]; 7 for(;;) 8 { 9 /* クライアントから送られるファイル名をソケットから読み込む */ 10 recv(socd, filename, MAXFILENAME+1, 0); 11 /* ファイルを読み出し専用にオープンする */ 12 if((fd = fopen(filename, "r")) != NULL) { /* ファイルオープンに成功した場合 */ 13 /* オープン成功メッセージを送る */ 14 ack = OK; 15 send(socd, &ack, 1, 0); 16 /* ファイルから1行読み込みソケットに書き出すことをEOFを読むまで繰り返す */ 17 printf("ファイル %s を送信\n",filename); 18 while(fgets(buf, MAXBUFLEN, fd)) { 19 send(socd, buf, strlen(buf), 0); 20 } 21 close(fd); 22 printf("\n"); 23 } 24 else 25 { /* ファイルオープンに失敗した場合 */ 26 /* オープン失敗メッセージを送る */ 27 ack = ERR; 28 send(socd, &ack, 1, 0); 29 } 30 } 31}

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

c

1void receive_file(int socd) /* サーバから受け取ったファイルの内容を表示する */ 2{ 3 char filename[MAXFILENAME+1]; 4 int filename_len; 5 char ack; 6 char buf[MAXBUFLEN]; 7 int length; 8 for(;;) 9 { 10 11 /* ファイル名の入力 */ 12 printf("remote file name?: "); 13 printf("what"); 14 if(scanf("%s",filename)==EOF)break; 15 printf("at"); 16 /* ファイル名をソケットに書き込む */ 17 filename_len = strlen(filename); 18 printf("that"); 19 send(socd, filename, filename_len+1, 0); 20 printf("chat"); 21 /* ファイルオープンに成功したかどうかのメッセージをソケットから読み込む */ 22 recv(socd, &ack, 1, 0); 23 printf("hat"); 24 switch (ack) { 25 case OK: /* ファイルオープンに成功した場合 */ 26 printf("ファイル %s を受信\n", filename); 27 /* ソケットから読み込み標準出力に書き出す */ 28 while(length = recv(socd, buf, MAXBUFLEN, 0)) { 29 buf[length] = '\0'; 30 fputs(buf, stdout); 31 break; 32 } 33 break; 34 case ERR: /* ファイルオープンに失敗した場合 */ 35 fprintf(stderr, "File access error\n"); 36 break; 37 } 38 } 39}

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tatsu99

2018/10/25 13:32

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

2018/10/25 14:55

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

2018/10/25 16:33

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

回答1

0

ベストアンサー

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

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

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

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

引用元

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

c

1#include <stdio.h> 2#include <string.h> 3 4int main() 5{ 6 char str[] = "file1.txt,file2.txt,file3.txt"; 7 8 char *filename = strtok(str, ","); 9 // 1回目の呼び出しでは、トークン、つまり先頭のポインタを返す。 10 // 見つけた区切り文字は\0で置き換えられるので、filename = "file1.txt" となっている。 11 12 do { 13 printf("%s\n", filename); 14 // 2回目以降の呼び出しのときは、第一引数は NULL を指定する。 15 // filename = "file2.txt" となっている。 16 // その次の呼び出した場合、 17 // filename = "file3.txt" となっている。 18 // その次の呼び出した場合、もうトークンが残ってないので NULL を返す。 19 } while (filename = strtok(NULL, ",")); 20}
file1.txt file2.txt file3.txt

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

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

投稿2018/10/25 10:00

編集2018/10/25 10:01
tiitoi

総合スコア21954

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問