weemiee
@weemiee (weemiee)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

期待通りにソケット通信出来ない

Q&A

Closed

次のソケット通信プログラムについて質問です。

サーバ
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define SERVER_PORT 8080
#define BUF_SIZE 1024

void transfer(int tf_sock){
	
	char send_buf[100], recv_buf[100];
	int send_size, recv_size;
	
	while (1) {
		
		recv_size = recv(tf_sock, recv_buf, sizeof(recv_buf), 0);
		
		if (recv_size == -1) {
			printf("recv error\n");
			exit(1);
		}
		
		if (sizeof(recv_buf) == 0) {
			continue;
		}
		
		printf("%s\n", recv_buf);
		
	}
	
}

int main () {
	
	int sock_s = socket(AF_INET, SOCK_STREAM, 0);   //ソケットを作成
	if (sock_s == -1) {
		printf("ソケットエラーです\n");
		return -1;
	}
	
	//IPアドレスやポート番号の指定
	struct sockaddr_in sv_addr;
	memset(&sv_addr, 0, sizeof(struct sockaddr_in));
	sv_addr.sin_family = AF_INET;
	sv_addr.sin_port = htons((unsigned short)SERVER_PORT);
	sv_addr.sin_addr.s_addr = INADDR_ANY;
	
	if ( bind(sock_s, (struct sockaddr *)&sv_addr, sizeof(sv_addr)) == -1 ) {   //ソケット登録
	printf("bind error\n");
		close(sock_s);
		return -1;
	}
	
	while (1) {
		
		if ( listen(sock_s, SOMAXCONN) == -1 ) {   //接続待ち
			printf("listen error\n");
			close(sock_s);
			return -1;
		}
		
		printf("Waiting connect...\n");
		struct sockaddr_in get_addr;
		socklen_t len = sizeof(struct sockaddr_in);
		int c_sock_s = accept(sock_s, (struct sockaddr *)&get_addr, &len);   //接続要求の受け付け
		
		if (c_sock_s == -1) {
			printf("accept error\n");
			close(sock_s);
			return -1;
		}
		
		printf("Connected successfully.\n");
		transfer(c_sock_s);   //通信
		close(c_sock_s);
		
	}
	
	//クローズ
	close(sock_s);
	
}
クライアント
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define SERVER_PORT 8080
#define BUF_SIZE 1024

void transfer(int tf_sock){
	
	char send_buf[100], recv_buf[100];
	int send_size, recv_size;
	
	while(1){
		
		printf("Input Message here : ");
		scanf("%s", send_buf);   //サーバーへ送信する文字列を キーボードより読み込む
		
		if (sizeof(send_buf) == 0) {
			continue;
		}
		
		send_size = send(tf_sock, send_buf, sizeof(send_buf), 0);   //送信
		
		if (send_size == -1) {
			printf("send error\n");
			exit(1);
		}
		
	}
	
}

int main(){
	
	int sock_c = socket(AF_INET, SOCK_STREAM, 0);   //ソケットを作成
	
	if (sock_c == -1) {
		printf("ソケットエラーです\n");
		return -1;
	}
	
	//IPアドレスやポート番号の指定
	struct sockaddr_in ce_addr;
	memset(&ce_addr, 0, sizeof(struct sockaddr_in));
	ce_addr.sin_family = AF_INET;
	ce_addr.sin_port = htons((unsigned short)SERVER_PORT);
	ce_addr.sin_addr.s_addr = INADDR_ANY;
	
	printf("Start connecting...\n");
	
	if ( connect(sock_c, (struct sockaddr*)&ce_addr, sizeof(struct sockaddr_in)) == -1 ) {   //サーバーに接続要求送信
		printf("connect error\n");
		close(sock_c);
		return -1;
	}
	
	printf("Finish connect.\n");
	transfer(sock_c);   //通信
	close(sock_c);   //クローズ
	
}

クライアント側でInput Message here : と表示されて直ぐにエンターキーを押した(何も文字を入力しなかった)場合には、ループの冒頭に戻ってInput Message here : と聞き返してくれるのを期待していました。しかし、結果は下図の通り。

⇩クライアント側
127.0.0.1 - pi@raspberry_ ~ VT 2022_06_08 12_31_09.png

⇩サーバ側
Audials TV Series 2022_06_08 12_31_56.png

文字列「vwx」を送信した後のInput Message here : で、試しにそのままエンターしたら、改行されただけで画面には何も表示されず、2回ほど ”改行” した後に「y」を入れたら、ここで急にInput Message here : が飛び出してきた感じです。
上のバグを受けて、if文周辺を重点的にあたり、試行錯誤はしてみたものの、結果は上図のままで、どうしても改善しません。未入力で送信したら都度聞き返してくれるプログラムを求めています。アドバイスをよろしくお願いします。

0

5Answer

文字列「vwx」を送信した後のInput Message here : で、試しにそのままエンターしたら、改行されただけで画面には何も表示されず、2回ほど ”改行” した後に「y」を入れたら、ここで急にInput Message here : が飛び出してきた感じです。

scanfは改行を区切り文字として扱うため、改行のみだと入力として認識されていないのが原因かと思います。(以下などが参考になると思います)
https://qiita.com/mpyw/items/aff12a6ff2c7726ed1d8

未入力で改行(送信)した場合に、都度聞き返すようなものであれば、例えば以下のように、fgetsstrtokを使ったものはいかがでしょうか。
(動作確認はちゃんとしていないので、ご参考まで)

クライアント
void transfer(int tf_sock){

    char send_buf[100], recv_buf[100];
    int send_size, recv_size;

    while(1){

        printf("Input Message here : ");
//      scanf("%s", send_buf);   //サーバーへ送信する文字列を キーボードより読み込む
        // fgetsで改行も入力として扱い、strtokを使って改行で入力を分解する(=改行の前までの文字取得)
        fgets(send_buf, 100, stdin);
        strtok(send_buf, "\n");

        if (sizeof(send_buf) == 0) {
            continue;
        }

    // 以下略
    }
}
1Like

とりあえず、

if (sizeof(send_buf) == 0) {
  continue;
}

trueになることはないので、何も入力せずにEnter押してもcontinueに入ることはないです。

1Like

Comments

  1. @weemiee

    Questioner

    上のif文がtrueになるケースがないのは、何故なのでしょうか?
  2. sizeof演算子が文字列長を取得するものではないからです(sizeofの詳細はWeb上にいくらでもあるので一読することをお勧めします)。
    文字列長を取得したいのであれば、strlenやstrnlenを利用してください。

@marumenさん、先日はご回答ありがとうございます。
読み込みの関数をscanfからfgetsに変更後、クライアント側は未入力でも一回一回聞き返してくれるようになり、助かりました。
しかし、その代わり (?) サーバ側で問題が発生している点に気づいてしまいました。

クライアント側でInput Message here :に対して何も文字を入れずにエンターすると、サーバ側で不自然に行が開いてしまう動作について調べるため、上の質問のサーバ側のプログラムで、while文最下部のprintf文 (画面表示) を以下のように変えてみました。

printf("入力した文字列は%sです\n", recv_buf);

これで実行すると、未入力でエンターしてもサーバ画面に「入力した文字列は」と「です」が表示されてしまうのです。(※途中改行されているのは、fgetsによる改行 “入力” によるものとみている)

入力した文字列は
です

この、未入力時にもかかわらずcontinueを明らかに無視したような挙動に頭を悩ませていた時、@mrbonjinさんのご回答が気にかかりました。

次の部分

if (sizeof(send_buf) == 0) {
	continue;
}

が真になるケースがないのは何故なのか、こちらについて@marumenさんにもご回答を仰げたらと思います。
どうかアドバイスをよろしくお願いします。

0Like

私が回答したときになぜ気づかなかったのかという感じですが、sizeofについては、@mrbonjinさんがご回答された通りです。
strlenstrnlenに置き換えれば、今困られている問題は解決すると思います。

0Like

@mrbonjinさん、@marumenさん、ご回答ありがとうございました。
本当に貴重なアドバイスで、伺えて幸いです。sizeof演算子について、書籍やWEBを用いて調べてみようと思います。

0Like

Your answer might help someone💌