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

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

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

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

Q&A

解決済

3回答

3827閲覧

C言語でクライアント・サーバで通信をするプログラムについて

maikeru

総合スコア68

C

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

0グッド

0クリップ

投稿2019/05/24 13:05

C言語でソケットを利用するプログラムを書いていて、クライアントプログラムでファイル名を送信して、サーバプログラムが受け取ったファイル名のファイルを開きその中身を送信するプログラムを作成しました。
クライアントプログラムの実行結果は次のようになり、クライアント、サーバプログラムを動作させ、クライアントでファイル名を送信するとサーバ側でファイルを開きその中身をループして送信してくれるのですがサーバがファイルの中身をすべて送信し終わってもクライアントはwhileループでrecv()で待ち続けているためか次のファイル名の入力ができない状態になっています。そのため次の実行例では^Cで強制終了しています。どのように修正すれば正しいプログラムになるでしょうか。

input filename: test.txt aaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbccccc ddddekekkkkkkkk 000fffff44444fccccc ^C

クライアント側のプログラム

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#include <sys/socket.h> 6#include <sys/types.h> 7#include <arpa/inet.h> 8#include <errno.h> 9#include <unistd.h> 10#include <netdb.h> 11#include <netinet/in.h> 12#include <fcntl.h> 13 14#define PORT 8888 15#define BUFSIZE 128 16 17int main(int argc, char *argv[]){ 18 struct sockaddr_in addr; 19 int fd, n; 20 char buf[BUFSIZE]; 21 22 if((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ 23 perror("socket"); 24 exit(-1); 25 } 26 27 memset(&addr, 0, sizeof(addr)); 28 addr.sin_family = AF_INET; 29 addr.sin_port = htons(PORT); 30 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 31 32 if(connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){ 33 perror("connect"); 34 exit(-1); 35 } 36 37 while(1){ 38 printf("input filename: "); 39 fgets(buf, BUFSIZE, stdin); 40 send(fd, buf, BUFSIZE, 0); 41 while((n = recv(fd, buf, BUFSIZE, 0)) > 0) // ファイルをすべて読み出してもここから抜けられない? 42 write(1, buf, n); 43 } 44 45 close(fd); 46 47 return 0; 48}

サーバ側のプログラム

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#include <sys/socket.h> 6#include <sys/types.h> 7#include <arpa/inet.h> 8#include <errno.h> 9#include <unistd.h> 10#include <netdb.h> 11#include <netinet/in.h> 12#include <fcntl.h> 13 14#define PORT 8888 15#define BUFSIZE 128 16 17int main(int argc, char *argv[]){ 18 struct sockaddr_in saddr, caddr; 19 int fd1, fd2, fp, n, ret, len; 20 char buf[BUFSIZE]; 21 char *c; 22 23 if((fd1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ 24 perror("socket"); 25 return -1; 26 } 27 28 memset(&saddr, 0, sizeof(saddr)); 29 saddr.sin_family = AF_INET; 30 saddr.sin_port = htons(PORT); 31 saddr.sin_addr.s_addr =htonl(INADDR_ANY); 32 33 if(bind(fd1, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){ 34 perror("bind"); 35 exit(-1); 36 } 37 38 if(listen(fd1, 5)){ 39 perror("listen"); 40 return -1; 41 } 42 43 if((fd2 = accept(fd1, (struct sockaddr*)&caddr, &len)) < 0){ 44 perror("accept"); 45 return -1; 46 } 47 48 49 while(1){ 50 recv(fd2, buf, BUFSIZE, 0); 51 c = strchr(buf, '\n'); if(c != NULL) *c = '\0'; //remove '\n' 52 printf("filename : [%s]\n", buf); 53 if((fp = open(buf, O_RDONLY, 0644)) < 0){ 54 perror("open"); 55 exit(-1); 56 } 57 while((n = read(fp, buf, sizeof(buf)))) 58 send(fd2, buf, n, 0); 59 } 60 61 close(fd1); close(fd2); 62 63 return 0; 64}

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

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

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

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

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

guest

回答3

0

(違うところに回答してしまいました。申し訳ありません)

投稿2020/01/17 00:58

編集2020/01/17 01:04
shiracamus

総合スコア5406

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

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

0

ベストアンサー

recv は(戻り値としてバイト数を返す通り)一度の呼び出しで何バイト取り込めるか分かりません.
テスト時はたまたま短い文字列(ファイル名)だったので一度に読めたようですが, 最悪何度 recv しても 1バイトずつしか取り込めない可能性もあるということです.
もしクライアントが 'test.txt' と送信してもサーバの recv(fd2, buf, BUFSIZE, 0); で( recv が 3 を返し) buf に 'tes' までしか入っていなかったら, どうなりますか.
そのような状態でファイル名やファイルデータを受け取るには, 2つの方法があります.

  1. まずサイズを送信/受信し, 次にそのサイズ分のデータを送信/受信する

ファイル名を送信する場合: 8,'test.txt'
ファイルの中身を送信する場合: (ファイルサイズ),'aaaaa...bbbb....'

  1. 「終わりを示すバイト」を定義し, 送信時は送信後にそのバイトを送信し, 受信時はそのバイトが来るまで受信する(「終わりを示すバイト」は捨てる)

(「終わりを示すバイト」='\0' )
ファイル名を送信する場合: 'test.txt','\0'
ファイルの中身を送信する場合: 'aaaaa...bbbb....','\0'

1 の場合, 例えばサイズを表す部分を 4 バイトの int とすると, 8文字を送るために12バイトが必要になります. 2 の方法なら 9 バイトです.
そしてもちろん, そのサイズを表す int も4バイトが一度に得られるとは限りません. byte ならその心配はありませんが, 長さとしては 255 までとなります.
2 の場合, 「終わりを示すバイト」がデータとして使えなくなるため, バイナリファイルには使えません. (エスケープを使えば出来ます.)
クライアントからサーバへのファイル名送信には 2 を使い, サーバからクライアントへのデータの送信には 1 を使うといった混在は可能です.

投稿2019/05/24 17:02

jimbe

総合スコア12545

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

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

0

何らかの方法でファイルの送信終了を通知しましょう。

投稿2019/05/24 13:09

y_waiwai

総合スコア87719

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問