通信について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
マーチ

通信について

#1

投稿記事 by マーチ » 9年前

まだ別の質問が解決していませんが、この課題も提出期限が切れてしまい、
解決策が見つからないため新たに質問させていただきます。申し訳ございません。

UDPを利用したソケット通信で、入力した文字列、送信された文字列をテキストに出力するプログラムを作成しております。

問題点は

・endを入力しても終わらない

・繰り返し入力をしたいのにできない
受信側は1回目の入力でrecvfrom: Bad file descriptor
送信側は2回目の入力でsendto: Bad file descriptor
が表示され強制終了されてしまいます。


C言語を数週間くらいしか触っていないほどの初心者ではありまずが、
何卒ご指摘助言よろしくお願いいたします。

コードを書き込ませていただきます。

受信プログラム

コード:

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

int main(int argc, char** argv)
{
    int sd;
    struct sockaddr_in addr;

    socklen_t sin_size;
    struct sockaddr_in from_addr;

    char buf[2048]; // 受信バッファ

    // IPv4 UDP のソケットを作成
    if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }

    // 待ち受けるIPとポート番号を設定
    addr.sin_family = AF_INET;
    addr.sin_port = htons(22222);
    addr.sin_addr.s_addr = INADDR_ANY; // すべてのアドレス宛のパケットを受信する

    // バインドする
    if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        return -1;
    }

while (1) {

    // 受信バッファの初期化
    memset(buf, 0, sizeof(buf));

    // 受信 パケットが到着するまでブロック
    // from_addr には、送信元アドレスが格納される
    if(recvfrom(sd, buf, sizeof(buf), 0,
                (struct sockaddr *)&from_addr, &sin_size) < 0) {
        perror("recvfrom");
        return -1;
    }

    // ソケットのクローズ
    close(sd);

    // 受信データの出力
    printf("%s\n", buf);
    
    FILE *fp;    //ファイルポインター

        fp = fopen("test2.dat","a"); //ファイルを開く

        //ファイルに書きこむ
        fprintf(fp,"%s\n",buf); //区切り文字:スペース
        //画面に出力
        //printf("%s\n",buf);

        fclose(fp); //ファイルを閉じる
        
        if (!strncmp(buf, "end", 3)) break;}
    
    return 0;
}
送信プログラム

コード:

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

char message[256];

int main(int argc, char** argv)
{
    int sd;
    struct sockaddr_in addr;

    if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }

    // 送信先アドレスとポート番号を設定する
    // 受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
    addr.sin_family = AF_INET;
    addr.sin_port = htons(22222);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

while (1) {

	printf(">>message\n");
    fgets(message, sizeof(message), stdin);

    // パケットをUDPで送信
    if(sendto(sd, message, 256, 0,
              (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("sendto");
        return -1;
    }
    
        FILE *fp;    //ファイルポインター

        fp = fopen("test.dat","a"); //ファイルを開く

        //ファイルに書きこむ
        fprintf(fp,"%s\n",message); //区切り文字:スペース
        //画面に出力
        //printf("%s\n",message);

        fclose(fp); //ファイルを閉じる
    
    close(sd);

if (!strncmp(message, "end", 3)) break;}

    return 0;
}

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#2

投稿記事 by みけCAT » 9年前

まずインデントを整えるべきだと思います。

送信、受信ともに、1回送信/受信したらソケットをcloseしてしまっているので、その後はBad file descriptorになっているのだと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#3

投稿記事 by みけCAT » 9年前

受信において、sin_sizeを初期化していないのもまずいかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#4

投稿記事 by マーチ » 9年前

ありがとうございます。
直してみます

マーチ

Re: 通信について

#5

投稿記事 by マーチ » 9年前

以前の問題は解決したのですが、また別の問題が出てしまい困っております。
よろしければまたご指摘助言よろしくお願いいたします。

問題点
・ポート番号をログに出力したい
調べてもポート番号のみ見つからず・・・

・設定ファイルからTCP/UDPとポート番号を変えたい
そもそも設定ファイルが調べてもわからず。

・受信プログラムのIPアドレスがログに出力できない
recvfrom: Invalid argumentと表示される。解釈できない不正なパラメータとはどこでしょうか・・・

・sin_sizeの初期化がわからない
みけCATさんからの指摘ですが方法がわからず・・・


またコードを書き込ませていただきます。よろしくお願いいたします。

受信プログラム

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <time.h>

int main(int argc, char** argv)
{
	int sd;
	//構造体の宣言
	struct sockaddr_in addr;

	//アドレスの長さを格納
	socklen_t sin_size;
	struct sockaddr_in from_addr;

	// 受信バッファ
	char buf[2048]; 

	// IPv4 UDP のソケットを作成
	if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	// 待ち受けるIPとポート番号を設定
	addr.sin_family = AF_INET;
	addr.sin_port = htons(22222);

	// すべてのアドレス宛のパケットを受信する
	addr.sin_addr.s_addr = INADDR_ANY; 

	// バインドする
	if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return -1;
	}

	while (1) {

		// 受信バッファの初期化
		memset(buf, 0, sizeof(buf));

		// 受信 パケットが到着するまでブロック
		// from_addr には、送信元アドレスが格納される
		if(recvfrom(sd, buf, sizeof(buf), 0,
			(struct sockaddr *)&from_addr, &sin_size) < 0) {
				perror("recvfrom");
				return -1;
		}

		// 受信データの出力
		printf("endが入力された場合、終了します\n");
		printf(">>message\n");

		printf("%s", buf);

		FILE *fp;    //ファイルポインター

		fp = fopen("test2.dat","a"); //ファイルを開く

		time_t now = time(NULL);
		struct tm *pnow = localtime(&now);

		char buff[128]="";

		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

		unsigned long long_addr = 2085852108ul;
		struct in_addr inaddr = { htonl(long_addr) };
		char *addrstr = inet_ntoa(inaddr);

		//ファイルに書きこむ
		fprintf(fp,"%s",buf); //区切り文字:スペース
		fprintf(fp,"%s\n",buff);
		fprintf(fp,"%s\n",addrstr);
		fprintf(fp,"\n");

		fclose(fp); //ファイルを閉じる

		if (!strncmp(buf, "end", 3)) break;

	}

	return 0;
}
送信プログラム

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

char message[256];

int main(int argc, char** argv)
{
	int sd;
	int inet_addr();
	struct sockaddr_in addr;
	int inet_ntoa();

	if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return -1;
	}

	// 送信先アドレスとポート番号を設定する
	// 受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
	addr.sin_family = AF_INET;
	addr.sin_port = htons(22222);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	while (1) {

		printf("メッセージは何にしますか?\n");
		printf("終了する場合はendを入力してください\n");
		fgets(message, sizeof(message), stdin);

		// パケットをUDPで送信
		if(sendto(sd, message, 256, 0,
			(struct sockaddr *)&addr, sizeof(addr)) < 0) {
				perror("sendto");
				return -1;
		}

		FILE *fp;    //ファイルポインター

		fp = fopen("test.dat","a"); //ファイルを開く

		time_t now = time(NULL);
		struct tm *pnow = localtime(&now);

		char buff[128]="";

		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

		unsigned long long_addr = 2085852108ul;
		struct in_addr inaddr = { htonl(long_addr) };
		char *addrstr = inet_ntoa(inaddr);

		//ファイルに書きこむ
		fprintf(fp,"%s",message); //区切り文字:スペース
		fprintf(fp,"%s\n",buff);
		fprintf(fp,"%s\n",addrstr);
		fprintf(fp,"\n");

		fclose(fp); //ファイルを閉じる

		if (!strncmp(message, "end", 3)) break;

	}

	return 0;
}
どうかよろしくお願いいたします。

Poco
記事: 161
登録日時: 13年前

Re: 通信について

#6

投稿記事 by Poco » 9年前

マーチ さんが書きました: 問題点
・ポート番号をログに出力したい
調べてもポート番号のみ見つからず・・・
ソースを見る限り22222番ポート固定なのですが、これを変更可能にする前提でいえば、
単純にポート番号を格納する変数を用意して、
ポート番号が確定した後、その変数の内容を出力すればよろしいかと。
マーチ さんが書きました: ・設定ファイルからTCP/UDPとポート番号を変えたい
そもそも設定ファイルが調べてもわからず。
設定ファイルとは何かを決めるのはマーチさん自身です。
とりあえず、テキストファイルに
 ・1行目には、tcpかudpという文字列を
 ・2行目には、ポート番号を書き込んで、
プログラムからfopen,fread(fscanf)等を利用して、それを読みだすようにしてみては?

設定ファイルを読み込んで、プロトコルとポート番号を返す処理を関数として別だしにすれば
その後で設定ファイルの中身も変えやすくなると思います。
マーチ さんが書きました: ・受信プログラムのIPアドレスがログに出力できない
recvfrom: Invalid argumentと表示される。解釈できない不正なパラメータとはどこでしょうか・・・

・sin_sizeの初期化がわからない
みけCATさんからの指摘ですが方法がわからず・・・
recvfrom()でsin_sizeを使用していますが、これは直前の引数from_addrのサイズを格納して渡さないといけません。
#man 2 recvfromでマニュアルを引けば書かれていると思います。
これで、送信側から接続されたときに、送信側の情報がfrom_addrに格納されます。from_addrはstruct sockaddr_in型なので、メンバsin_addrのメンバs_addrに
IPアドレスが格納されています。

マーチ

Re: 通信について

#7

投稿記事 by マーチ » 9年前

ポートは固定でなければ通信できない気がするのですが違いますかね?

fopenなどを調べたのですが文字列などを読み出すようなものしか見つかりませんでした。
どのように読み込むのでしょうか・・・

s_addrに変えたのですが文字化けしたなにかが出力され、IPアドレスは出力できませんでした・・・

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#8

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:ポートは固定でなければ通信できない気がするのですが違いますかね?
違います。送信側と受信側で一致していれば、別に固定でなくても通信できます。
マーチ さんが書きました:fopenなどを調べたのですが文字列などを読み出すようなものしか見つかりませんでした。
どのように読み込むのでしょうか・・・
もしscanfをご存知であれば、同じように読み込みができます。

コード:

#include <stdio.h>

int main(void) {
	const char* fileName = "test.txt";
	char str_data[1024];
	int int_data;
	FILE* fp;
	fp = fopen(fileName, "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	fscanf(fp, "%s", str_data);
	fscanf(fp, "%d", &int_data);
	fclose(fp);

	printf("str_data = %s\n", str_data);
	printf("int_data = %d\n", int_data);
	return 0;
}
これは、

コード:

文字列
整数
というフォーマットのテキストファイル"test.txt"を読み込むサンプルです。
文字列は空白を含まず、1023バイト以下でなくてはいけません。
厳密にエラーチェックをしたければ、fscanfではなくgetcなどの関数を用いてください(省略します)。
マーチ さんが書きました:s_addrに変えたのですが文字化けしたなにかが出力され、IPアドレスは出力できませんでした・・・
本当にIPアドレスは出力できていませんか?
IPアドレスのデータが数値(数字ではない)で出力されているのではないですか?

どうやって出力しようとしたかはわかりませんが、本当に出力できていない可能性もありますね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Poco
記事: 161
登録日時: 13年前

Re: 通信について

#9

投稿記事 by Poco » 9年前

マーチ さんが書きました: s_addrに変えたのですが文字化けしたなにかが出力され、IPアドレスは出力できませんでした・・・
情報不足です。
どういう風に出力しようとしたのかコードで示してください。
#s_addrの型は調べましたか?
#文字列ではないですよ。

かずま

Re: 通信について

#10

投稿記事 by かずま » 9年前

受信プログラムと送信プログラムのコンパイル時に
char *addrstr = inet_ntoa(intaddr);
の行で warning が出ませんでしたか?

#include <arpa/inet.h> を追加しましょう。
送信プログラムで勝手に
int inet_ntoa();
なんて宣言してはいけません。

unsigned long long_addr = 2085852108ul;
このアドレスをログに書くことが意味不明です。
受信プログラムのほうは直しておきました。

受信プログラム

コード:

4a5
> #include <arpa/inet.h>   // 追加
51a53
>         sin_size = sizeof from_addr;                      // 追加
75,77c77
<         unsigned long long_addr = 2085852108ul;
<         struct in_addr inaddr = { htonl(long_addr) };
<         char *addrstr = inet_ntoa(inaddr);
---
>         char *addrstr = inet_ntoa(from_addr.sin_addr);  // 変更
送信プログラム

コード:

4a5
> #include <arpa/inet.h>    // 追加
17c18
<     int inet_addr();
---
>     // int inet_addr();  // 削除
19c20
<     int inet_ntoa();
---
>     // int inet_ntoa();  // 削除

マーチ

Re: 通信について

#11

投稿記事 by マーチ » 9年前

みけCATさん
ランダムでも合わせれば通信可能ということですかね?
わかりやすい例をありがとうございます。
IPアドレスの件ですが、文字化けではなく「__libc_start_main」というのが出力されていました。
今はちゃんと出力は出来るようになっています。自宅に環境がなくあやふやなことを記載してしまい申し訳ありません。

Pocoさん
申し訳ありません。型は調べたのですが英語のページばかりでわからず・・・
それと文字化けではなく「__libc_start_main」というのが出力されていました。
今はちゃんと出力は出来るようになりました。自宅に環境がなくあやふやなことを記載してしまい申し訳ありません。

かずまさん
いつもありがとうございます。おっしゃるとおり警告文がいくつか表示されていました。
インデントがあるのですね。送信のほうはunsigned long long_addr = 2085852108ul;が残ってはいますがいくつか問題を解決することが出来ました、ありがとうございます。



いくつかは解決できたのですが、やはり設定ファイルで切り替える方法が全くわかりません。
そしてもうひとつ、ログ出力を一行でまとめたいのですが、入力した文字列に改行が入っており一行になりません。printfは改行を含まないと記憶しているのですがどこを間違えているのでしょうか?

再びコードを書き込ませていただきます。

受信プログラム

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
	int sd;
	//構造体の宣言
	struct sockaddr_in addr;
	//アドレスの長さを格納
	socklen_t sin_size;
	struct sockaddr_in from_addr;
	//受信バッファ
	char buf[2048];

	//UDP のソケットを作成
	if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return -1;
	}
	//待ち受けるIPとポート番号を設定
	addr.sin_family = AF_INET;
	addr.sin_port = htons(22222);  

	//すべてのアドレス宛のパケットを受信する
	addr.sin_addr.s_addr = INADDR_ANY;
	//バインドする
	if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return -1;
	}
	while (1) {
		// 受信バッファの初期化
		memset(buf, 0, sizeof(buf));
		// 受信 パケットが到着するまでブロック
		// from_addr には、送信元アドレスが格納される
		if(recvfrom(sd, buf, sizeof(buf), 0,
			(struct sockaddr *)&from_addr, &sin_size) < 0) {
				perror("recvfrom");
				return -1;
		}
		// 受信データの出力
		printf("exitが入力された場合、終了します\n");
		printf(">>message ");

		printf("%s", buf);
		FILE *fp;    //ファイルポインター
		fp = fopen("test.txt","a"); //ファイルを開く

		time_t now = time(NULL);
		struct tm *pnow = localtime(&now);
		char buff[128]="";
		sin_size = sizeof from_addr;

		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

		char *addrstr = inet_ntoa(from_addr.sin_addr);

		//ファイルに書きこむ
		fprintf(fp,"message %s",buf); //区切り文字:スペース
		fprintf(fp," time %s",buff);
		fprintf(fp," address %s",addrstr);
		fprintf(fp,"\n");

		fclose(fp); //ファイルを閉じる
		if (!strncmp(buf, "exit", 4)) break;

	}
	//ソケットのクローズ
	close(sd);

	return 0;
}
送信プログラム

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
	int sd;
	//構造体の宣言
	struct sockaddr_in addr;
	char message[256];
	//UDP のソケットを作成
	if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return -1;
	}
	//送信先アドレスとポート番号を設定
	//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
	addr.sin_family = AF_INET;
	addr.sin_port = htons(22222);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	while (1) {
		printf("メッセージは何にしますか?\n");
		printf("終了する場合はexitを入力してください\n");
		fgets(message, sizeof(message), stdin);

		// パケットをUDPで送信
		if(sendto(sd, message, 256, 0,
			(struct sockaddr *)&addr, sizeof(addr)) < 0) {
				perror("sendto");
				return -1;
		}
		FILE *fp;    //ファイルポインター
		fp = fopen("test2.txt","a"); //ファイルを開く

		time_t now = time(NULL);
		struct tm *pnow = localtime(&now);
		char buff[128]="";

		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

		unsigned long long_addr = 2085852108ul;
		struct in_addr inaddr = { htonl(long_addr) };
		char *addrstr = inet_ntoa(inaddr);

		//ファイルに書きこむ
		fprintf(fp,"message %s",message); //区切り文字:スペース
		fprintf(fp," time %s",buff);
		fprintf(fp," address %s",addrstr);
		fprintf(fp,"\n");
		fclose(fp); //ファイルを閉じる

		int fclose(FILE *fp);

		if (!strncmp(message, "exit", 4)) break;
	}
	//ソケットのクローズ
	close(sd);

	return 0;
}
よろしくお願いいたします。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#12

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:みけCATさん
ランダムでも合わせれば通信可能ということですかね?
わかりやすい例をありがとうございます。
確かにランダムでも偶然一致すれば通信可能です。
オフトピック
実際に通信が成立するかは、双方が処理しているプロトコル
(TCP/UDPだけでなく、HTTPやSMTPなどのレベルも含めて)が一致しているかという問題もありますが。
マーチ さんが書きました:やはり設定ファイルで切り替える方法が全くわかりません。
どこがわからないですか?
設定ファイルを読み込んで、その内容を出力することはできますか?
マーチ さんが書きました:そしてもうひとつ、ログ出力を一行でまとめたいのですが、入力した文字列に改行が入っており一行になりません。printfは改行を含まないと記憶しているのですがどこを間違えているのでしょうか?
確かにprintfは自動で改行を追加しない仕様ですが、fgetsによる入力データに改行が含まれており、
それが「入力した文字列に改行が入っており」という結果を生んでいると思います。
改行をエスケープ(かわりに別の文字/文字列を用いて表現)して出力するといいかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 通信について

#13

投稿記事 by かずま » 9年前

受信プログラムで
sin_size = sizeof from_addr;
をとんでもないところに追加していますね。

Pocoさんの
> recvfrom()でsin_sizeを使用していますが、これは直前の引数from_addrのサイズを格納して渡さないといけません。
> #man 2 recvfromでマニュアルを引けば書かれていると思います。
というアドバイスを全く無視していますね。
マニュアルが日本語じゃなかったとすれば、
recvfrom または man recvfrom でググってみてください。
日本語マニュアルのページが先頭に出てくるはずです。
その中の recvfrom の引数の説明をちゃんと読んでください。

それから message の改行文字の問題ですが、並行処理のプログラムで
fgets(data.message, sizeof data.message, stdin);
if ((p = strchr(data.message, '\n'))) *p = 0;
と書いていた時の 2行目は意味も分からず書いていたのですか?

あと、message の長さにかかわらず、常に 256バイト送信していますが
それは分かってやっていることですか?

マーチ

Re: 通信について

#14

投稿記事 by マーチ » 9年前

改行の件は早とちりでした、申し訳ございません。
慌てておりprintfではなくfgetsを使用していたことに気付きませんでした。
ご迷惑をおかけして申し訳ございませんでした。


文字列を出力するのは調べたり、みけCATさんのコードで理解しましたが、
設定ファイルを読み込んで出力する方法がわかりません。

設定ファイルで通信方法とポート番号を切り替える方法が頭の中で考えられません。
ファイルからTCPなどの関数を出力するのか、予めコードに記載している状態でファイルから何かを出力するのか切り替えるのか、・・・。
そもそも関数などを出力することができるのか・・・どう書けば良いのかわかりません。


recvfromの件ですが、bufに格納するためにfrom_addrが必要なため、
直前の引数に渡さないければならないということで良いのでしょうか・・・

sin_size = sizeof from_addr;の場所は訂正しました、ありがとうございます。

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
    int sd;
    //構造体の宣言
    struct sockaddr_in addr;
    //アドレスの長さを格納
    socklen_t sin_size;
    struct sockaddr_in from_addr;
    //受信バッファ
    char buf[2048];
 
    //UDP のソケットを作成
    if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }
    //待ち受けるIPとポート番号を設定
    addr.sin_family = AF_INET;
    addr.sin_port = htons(49513);
 
    //すべてのアドレス宛のパケットを受信する
    addr.sin_addr.s_addr = INADDR_ANY;
    //バインドする
    if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        return -1;
    }
    while (1) {
        // 受信バッファの初期化
        memset(buf, 0, sizeof(buf));
        // 受信 パケットが到着するまでブロック
        // from_addr には、送信元アドレスが格納される
        
        sin_size = sizeof from_addr;
        
        if(recvfrom(sd, buf, sizeof(buf), 0,
            (struct sockaddr *)&from_addr, &sin_size) < 0) {
                perror("recvfrom");
                //ソケットのクローズ
    			close(sd);
                return -1;
        }
        // 受信データの出力
        printf("exitが入力された場合、終了します\n");
        printf(">>message ");
 
        printf("%s", buf);
        printf("\n");
        
        FILE *fp;    //ファイルポインター
        fp = fopen("test.txt","a"); //ファイルを開く
 
        time_t now = time(NULL);
        struct tm *pnow = localtime(&now);
        char buff[128]="";
 
        sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
 
        char *addrstr = inet_ntoa(from_addr.sin_addr);
 
        //ファイルに書きこむ
        fprintf(fp,"message:%s",buf); //区切り文字:スペース
        fprintf(fp," , time:%s",buff);
        fprintf(fp," , address:%s",addrstr);
        fprintf(fp,"\n");
 
        fclose(fp); //ファイルを閉じる
        if (!strncmp(buf, "exit", 4)) break;
 
    }
    //ソケットのクローズ
    close(sd);
 
    return 0;
}

マーチ

Re: 通信について

#15

投稿記事 by マーチ » 9年前

追記 常に 256バイト送信していたことは気付かなかったです。記載した分送信されるということなのですね・・・

かずま

Re: 通信について

#16

投稿記事 by かずま » 9年前

マーチ さんが書きました:recvfromの件ですが、bufに格納するためにfrom_addrが必要なため、
直前の引数に渡さないければならないということで良いのでしょうか・・・
TCP は、一旦接続したら、そのソケットをクローズするまで
ずっと接続状態が続きます。
データを受信すると、それがどこから送られてきたのかは分かっています。

UDP は TCP と違って、コネクションレスです。
データの受信を recvfrom() で待っていると、
どこからか何らかのデータが来ます。

socklen_t size = 256;
size_t n = recvfrom(sd, buf, 2048, 0 , &addr, &size);

これは、
受信データを入れる領域を buf に 2048バイト用意しました、
送信元のアドレスを入れる領域を addr に 256バイト用意しました、
ということを recvfrom に対して伝えて、
受信データと、送信元アドレスを取得するわけです。

実際に受信すると、受信データは用意した領域いっぱいになるとは限りません。
buf には受信データが入り、n に実際に受信したデータのサイズが返ります。

送信元のアドレスも addr に入り、そのサイズが size に返ります。

関数の返却値は、受信データのサイズなので、送信元アドレスのサイズは
引数である size に返ってきます。だから、&size と、size のアドレスを
渡しているのです。

size は、addr にどれだけのサイズの領域が用意されているのかを
recvfrom に教えてやらないといけないのです。
size を初期化せず、たまたまこの値がゼロだと、
addr に送信元アドレスは返ってきません。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#17

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:文字列を出力するのは調べたり、みけCATさんのコードで理解しましたが、
設定ファイルを読み込んで出力する方法がわかりません。
これの解答は自分のサンプルそのまんまだと思いますが・・・
みけCAT さんが書きました:

コード:

#include <stdio.h>

int main(void) {
	const char* fileName = "test.txt";
	char str_data[1024];
	int int_data;
	FILE* fp;
	fp = fopen(fileName, "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	fscanf(fp, "%s", str_data);
	fscanf(fp, "%d", &int_data);
	fclose(fp);

	printf("str_data = %s\n", str_data);
	printf("int_data = %d\n", int_data);
	return 0;
}
これは、

コード:

文字列
整数
というフォーマットのテキストファイル"test.txt"を読み込むサンプルです。
scanfを知らないのであれば、fgetsで読み込むことも可能です。

コード:

#include <stdio.h>

int main(void) {
	const char* fileName = "test.txt";
	char str_data[1024];
	char int_buffer[1024];
	int int_data;
	int negative_flag;
	int i;
	FILE* fp;
	/* ファイルを開く */
	fp = fopen(fileName, "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	/* 文字列データを読み込み、最後の改行を削除する */
	fgets(str_data, sizeof(str_data), fp);
	for (i = 0; str_data[i] != '\0'; i++) {
		if (str_data[i] == '\n') {
			str_data[i] = '\0';
			break;
		}
	}
	/* 整数データを読み込む */
	fgets(int_buffer, sizeof(int_buffer), fp);
	for (i = 0; int_buffer[i] == ' ' || int_buffer[i] == '\t'; i++); /* 先頭の空白をスキップ */
	negative_flag = (int_buffer[i] == '-'); /* 負数かを判定 */
	if (negative_flag) i++;
	for (int_data = 0; int_buffer[i] != '\0'; i++) {
		/* 数字を数値に変換する(オーバーフローのチェックは省略) */
		if (int_buffer[i] < '0' || '9' < int_buffer[i]) break;
		int_data = int_data * 10 + (int_buffer[i] - '0');
	}
	if (negative_flag) int_data = -int_data; /* 負数であるという情報を反映する */
	/* ファイルを閉じる */
	fclose(fp);

	/* 読み込んだデータを出力する */
	printf("str_data = %s\n", str_data);
	printf("int_data = %d\n", int_data);
	return 0;
}
マーチ さんが書きました:設定ファイルで通信方法とポート番号を切り替える方法が頭の中で考えられません。
ファイルからTCPなどの関数を出力するのか、予めコードに記載している状態でファイルから何かを出力するのか切り替えるのか、・・・。
そもそも関数などを出力することができるのか・・・どう書けば良いのかわかりません。
ファイルから出力ではなくて入力'(読み込み)をするべきだと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 通信について

#18

投稿記事 by かずま » 9年前

かずま さんが書きました: socklen_t size = 256;
size_t n = recvfrom(sd, buf, 2048, 0 , &addr, &size);
訂正します。

コード:

struct sockaddr_in addr;
socklen_t size = sizeof addr;  // sizeof(struct sockaddr_in) でも同じ
size_t n = recvfrom(sd, buf, 2048, 0 , (struct sockeaddr *)&addr, &size);
recvfrom の第4引数は struct sockaddr * ですが、
実際に、ここに使用するのは、struct sockaddr_in であったり
struct sockaddr_int6 だったりするわけです。
address family が AF_INET か、 AF_INET6 か、
AF_UNIX かなどで変わります。

コード:

sizeof(struct sockaddr) = 16
sizeof(struct sockaddr_in) = 16
sizeof(struct sockaddr_in6) = 28
sizeof(struct sockaddr_un) = 110
sizeof(struct sockaddr_storage) = 128

かずま

Re: 通信について

#19

投稿記事 by かずま » 9年前

また、訂正です。

size_t n = ... ではなく、ssize_t n = ... です。
recvfrom() は符号付きの値を返します。
かずま さんが書きました: recvfrom の第4引数は struct sockaddr * ですが、
第4引数ではなく、第5引数です。
この引数は、異なるサイズの構造体へのポインタということです。
それで、第6引数にサイズが必要なのです。

マーチ

Re: 通信について

#20

投稿記事 by マーチ » 9年前

かずまさん
サイズの領域を教えなければいけないということなのですね、あまり理解できずに使っておりました・・・

説明ありがとうございます。


みけCATさん
なにか関数を出力するのだと考えておりました。ファイルを使って入力を行えばば良いのですね。



少し設定ファイルを考えて書いてみましたが、疑問点があります。

・書いたコードは設定ファイルで切り替えていると言えるのでしょうか?
・受信プログラムにどうやってTCPとUDPを選んだのか教えるのかわかりません
・ポート番号で切り替えたいのですが反応しない
(今は正しく起動するために//にしています。
ポインタと比較している言われるのですが、正しい方法がわかりません)

コードを書き込ませていただきます。
tcp.txtとudp.txtにはポート番号としてそれぞれ、
49514、49513という数字を書いています。

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>

int int_data;
FILE* fp;

char handan[10];

int sd;
int acc_sd;
//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

socklen_t sin_size = sizeof(struct sockaddr_in);
//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

char buf[2048];
int main(void) 
{
	//-----本当はここからはいらない 入力は送信側の為-----

	printf("TCPなら0を、UDPなら1を入力してください.\n");
	fgets(handan, sizeof(handan), stdin);

	while(handan[0] != '0' && handan[0] != '1'){
		printf("0か1を入力してください.\n");
		fgets(handan, sizeof(handan), stdin);
	}
	if(handan[0] == '0'){
		const char* fileName = "tcp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%d", &int_data);
		fclose(fp);

		printf("int_data = %d\n", int_data);
	}
	if(handan[0] == '1'){
		const char* fileName = "udp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%d", &int_data);
		fclose(fp);

		printf("int_data = %d\n", int_data);
	}
	//-----本当はここまでいらない 入力は送信側の為-----

	//-----TCPの処理-----
	if(handan[0] == '0'){//-----下に変えたい。ポート番号で切り替えたいため-----
		//if(int_data == "49514"){
		// IPv4 TCP のソケットを作成
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 待ち受けるIPとポート番号を設定
		addr.sin_family = AF_INET;
		addr.sin_port = htons(49514);
		addr.sin_addr.s_addr = INADDR_ANY;

		// バインドする
		if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
			perror("bind");
			return -1;
		}

		// パケット受信待ち状態とする
		// 待ちうけキューを10としている
		if(listen(sd, 10) < 0) {
			perror("listen");
			return -1;
		}

		// クライアントからコネクト要求が来るまで停止する
		// 以降、サーバ側は acc_sd を使ってパケットの送受信を行う
		if((acc_sd = accept(sd, (struct sockaddr *)&from_addr, &sin_size)) < 0) {
			perror("accept");
			return -1;
		}

		while (1) {
			// 受信バッファの初期化
			memset(buf, 0, sizeof(buf));
			// パケット受信。パケットが到着するまでブロック
			if(recv(acc_sd, buf, sizeof(buf), 0) < 0) {
				perror("recv");
				return -1;
			}

			// 受信データの出力
			printf("exitが入力された場合、終了します\n");
			printf(">>message ");

			printf("%s", buf);
			printf("\n");

			FILE *fp;    //ファイルポインター
			fp = fopen("test.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			char *addrstr = inet_ntoa(from_addr.sin_addr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",buf); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");

			fclose(fp); //ファイルを閉じる
			if (!strncmp(buf, "exit", 4)) break;
		}
	}
	//-----UDPの処理-----
	if(handan[0] == '1'){//-----下に変えたい。ポート番号で切り替えたいため-----
		//if(int_data == "49513"){
		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//待ち受けるIPとポート番号を設定
		addr.sin_family = AF_INET;
		addr.sin_port = htons(49513);

		//すべてのアドレス宛のパケットを受信する
		addr.sin_addr.s_addr = INADDR_ANY;
		//バインドする
		if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
			perror("bind");
			return -1;
		}
		while (1) {
			// 受信バッファの初期化
			memset(buf, 0, sizeof(buf));
			// 受信 パケットが到着するまでブロック
			// from_addr には、送信元アドレスが格納される

			sin_size = sizeof from_addr;

			if(recvfrom(sd, buf, sizeof(buf), 0,
				(struct sockaddr *)&from_addr, &sin_size) < 0) {
					perror("recvfrom");
					//ソケットのクローズ
					close(sd);
					return -1;
			}
			// 受信データの出力
			printf("exitが入力された場合、終了します\n");
			printf(">>message ");

			printf("%s", buf);
			printf("\n");

			FILE *fp;    //ファイルポインター
			fp = fopen("test.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			char *addrstr = inet_ntoa(from_addr.sin_addr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",buf); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");

			fclose(fp); //ファイルを閉じる
			if (!strncmp(buf, "exit", 4)) break;

		}
	}
	// パケット送受信用ソケットのクローズ
	close(acc_sd);
	//ソケットのクローズ
	close(sd);

	return 0;

}

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>

int int_data;
FILE* fp;

char handan[10];

int sd;
//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

int main(void) 
{
	printf("TCPなら0を、UDPなら1を入力してください.\n");
	fgets(handan, sizeof(handan), stdin);

	while(handan[0] != '0' && handan[0] != '1'){
		printf("0か1を入力してください.\n");
		fgets(handan, sizeof(handan), stdin);
	}
	if(handan[0] == '0'){
		const char* fileName = "tcp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%d", &int_data);
		fclose(fp);

		printf("int_data = %d\n", int_data);
	}
	if(handan[0] == '1'){
		const char* fileName = "udp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%d", &int_data);
		fclose(fp);

		printf("int_data = %d\n", int_data);
	}
	//-----TCPの処理-----
	if(handan[0] == '0'){//-----下に変えたい。ポート番号で切り替えたいため-----
		//if(int_data == "49514"){
		// IPv4 TCP のソケットを作成する
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 送信先アドレスとポート番号を設定する
		addr.sin_family = AF_INET;
		addr.sin_port = htons(49514);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {

			printf("メッセージは何にしますか?\n");
			printf("終了する場合はexitを入力してください\n");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// サーバ接続(TCP の場合は、接続を確立する必要がある)
			connect(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

			// パケットを TCP で送信
			if(send(sd, message, 256, 0) < 0) {
				perror("send");
				return -1;
			}

			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}

	}
	//-----UDPの処理-----
	if(handan[0] == '1'){//-----下に変えたい。ポート番号で切り替えたいため-----
		//if(int_data == "49513"){
		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//送信先アドレスとポート番号を設定
		//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
		addr.sin_family = AF_INET;
		addr.sin_port = htons(49513);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {

			printf("メッセージは何にしますか?\n");
			printf("終了する場合はexitを入力してください\n");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// パケットをUDPで送信
			if(sendto(sd, message, 256, 0,
				(struct sockaddr *)&addr, sizeof(addr)) < 0) {
					perror("sendto");
					//ソケットのクローズ
					close(sd);
					return -1;
			}
			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}
	}
	//ソケットのクローズ
	close(sd);

	return 0;

}
どうかよろしくお願いいたします。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#21

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:・書いたコードは設定ファイルで切り替えていると言えるのでしょうか?
残念ながら言えないと思います。
TCP/UDPは設定ファイルではなく手動で切り替えてしまっていますし、せっかく読み込んだポート番号も反映されていません。
マーチ さんが書きました:・受信プログラムにどうやってTCPとUDPを選んだのか教えるのかわかりません
通信せずに教えるのは難しいので、TCPを受信するソケットとUDPを受信するソケットを両方作り、両方acceptすればいいと思います。
select関数を使うといいと思います。
マーチ さんが書きました:・ポート番号で切り替えたいのですが反応しない
(今は正しく起動するために//にしています。
ポインタと比較している言われるのですが、正しい方法がわかりません)
数字ではなく数値と比較してください。
すなわち、"49513"の代わりに49513と比較してください。
また、"49514"の代わりに49514と比較してください。

そもそも、
マーチ さんが書きました:・設定ファイルからTCP/UDPとポート番号を変えたい
と書いているのに、TCP/UDPの設定がポート番号に依存した仕様でいいのですか?
(マーチさんがいいと思うならそういう仕様でいいです)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#22

投稿記事 by マーチ » 9年前

設定ファイルで切り替えるということはどういうことなのでしょうか・・・

こういう方法しか思いつきませんでした・・・

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#23

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:設定ファイルで切り替えるということはどういうことなのでしょうか・・・
適当に書いてみました。コンパイル未確認です。受信も同様に読み込んで処理します。

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
 
FILE* fp;
 
char protocol[1024];
int port;
 
int sd;
//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;
 
int main(void) 
{
	fp = fopen("config.txt", "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	fscanf(fp, "%s", protocol);
	fscanf(fp, "%d", &port);
	fclose(fp);

	//-----TCPの処理-----
	if (strcmp(protocol, "TCP") == 0) {

		// IPv4 TCP のソケットを作成する
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 送信先アドレスとポート番号を設定する
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			// 省略
		}
	}
	//-----UDPの処理-----
	else if(strcmp(protocol, "UDP") == 0) {
		
		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//送信先アドレスとポート番号を設定
		//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			// 省略
		}
	}

	//ソケットのクローズ
	close(sd);

	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#24

投稿記事 by マーチ » 9年前

わざわざありがとうございます!
設定ファイルの使い方はこれで合っていると言われたのですが、
これでどう切り替えるのかがわからず・・・すみません。

TCPなら0を、UDPなら1を入力してくださいということにして
0ならtcpconfig.txt
1ならudpconfig.txt
を出そうという考えでしたがそれではだめなそうで・・・

コード:

TCP 49513
UDP 49514
で上と下を切り替えるようにしなくてはいけないようで書き方がわからず・・・

やはり設定ファイルで切り替えるという方法がよくわかりません・・・
現状のコードを記載させていただきます。

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>

FILE* fp;

char protocol[1024];
int port;

int sd;
int acc_sd;

//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

char buf[2048];

//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

int main(void) 
{
	fp = fopen("config.txt", "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	fscanf(fp, "%s %d", protocol, &port);
	fclose(fp);

	//-----TCPの処理-----
	if (strcmp(protocol, "TCP") == 0) {

		printf("exitが入力された場合、終了します\n");

		// IPv4 TCP のソケットを作成する
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 送信先アドレスとポート番号を設定する
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		// バインドする
		if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
			perror("bind");
			return -1;
		}

		// パケット受信待ち状態とする キュー:待ち行列
		// 待ちうけキューを10としている
		if(listen(sd, 10) < 0) {
			perror("listen");
			return -1;
		}

		// クライアントからコネクト要求が来るまで停止する
		// acc_sd を使ってパケットの送受信を行う
		if((acc_sd = accept(sd, (struct sockaddr *)&from_addr, &sin_size)) < 0) {
			perror("accept");
			return -1;
		}

		while (1) {
			// 受信バッファの初期化
			memset(buf, 0, sizeof(buf));
			// パケット受信。パケットが到着するまでブロック
			if(recv(acc_sd, buf, sizeof(buf), 0) < 0) {
				perror("recv");
				return -1;
			}

			// 受信データの出力
			printf(">>message ");

			printf("%s", buf);
			printf("\n");

			FILE *fp;    //ファイルポインター
			fp = fopen("test.txt","a"); //ファイルを開く

			//時間を保存
			time_t now = time(NULL);
			//現地時間(日本時間)に変換し、tm構造体に格納する
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			//時間をbuffに格納
			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
			//数値とコードを変換する
			char *addrstr = inet_ntoa(from_addr.sin_addr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",buf); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");

			fclose(fp); //ファイルを閉じる
			if (!strncmp(buf, "exit", 4)) break;
		}
	}
	//-----UDPの処理-----
	else if(strcmp(protocol, "UDP") == 0) {

		printf("exitが入力された場合、終了します\n");

		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//送信先アドレスとポート番号を設定
		//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		//すべてのアドレス宛のパケットを受信する
		addr.sin_addr.s_addr = INADDR_ANY;
		//バインドする
		if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
			perror("bind");
			return -1;
		}

		while (1) {
			// 受信バッファの初期化
			memset(buf, 0, sizeof(buf));
			// 受信 パケットが到着するまでブロック
			// from_addr には、送信元アドレスが格納される

			sin_size = sizeof from_addr;

			if(recvfrom(sd, buf, sizeof(buf), 0,
				(struct sockaddr *)&from_addr, &sin_size) < 0) {
					perror("recvfrom");
					//ソケットのクローズ
					close(sd);
					return -1;
			}
			// 受信データの出力
			printf(">>message:");

			printf("%s", buf);
			printf("\n");

			FILE *fp;    //ファイルポインター
			fp = fopen("test.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			char *addrstr = inet_ntoa(from_addr.sin_addr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",buf); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");

			fclose(fp); //ファイルを閉じる
			if (!strncmp(buf, "exit", 4)) break;
		}
	}

	//ソケットのクローズ
	close(sd);

	return 0;
}

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>

FILE* fp;

char protocol[1024];
int port;

int sd;
//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

int main(void) 
{
	fp = fopen("config.txt", "r");
	if (fp == NULL) {
		puts("file open error");
		return 1;
	}
	fscanf(fp, "%s %d", protocol, &port);
	fclose(fp);

	//-----TCPの処理-----
	if (strcmp(protocol, "TCP") == 0) {

		printf("終了する場合はexitを入力してください\n");

		// IPv4 TCP のソケットを作成する
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 送信先アドレスとポート番号を設定する
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			printf(">>message:");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// サーバ接続(TCP の場合は、接続を確立する必要がある)
			connect(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

			// パケットを TCP で送信
			if(send(sd, message, 256, 0) < 0) {
				perror("send");
				return -1;
			}

			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}
	}
	//-----UDPの処理-----
	else if(strcmp(protocol, "UDP") == 0) {

		printf("終了する場合はexitを入力してください\n");

		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//送信先アドレスとポート番号を設定
		//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			printf(">>message:");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// パケットをUDPで送信
			if(sendto(sd, message, 256, 0,
				(struct sockaddr *)&addr, sizeof(addr)) < 0) {
					perror("sendto");
					//ソケットのクローズ
					//close(sd);
					return -1;
			}
			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}
	}

	//ソケットのクローズ
	close(sd);

	return 0;
}
本当に無知で申し訳ありません。どうかよろしくお願いいたします。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#25

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:TCPなら0を、UDPなら1を入力してくださいということにして
0ならtcpconfig.txt
1ならudpconfig.txt
を出そうという考えでしたがそれではだめなそうで・・・

コード:

TCP 49513
UDP 49514
で上と下を切り替えるようにしなくてはいけないようで書き方がわからず・・・
仕様の確認なのですが、TCPかUDPかは手動で選択するのですか?
それとも、別の設定ファイルから読み込むのですか?
実はその他ですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#26

投稿記事 by マーチ » 9年前

返信ありがとうございます。

コード:

TCP 49513
UDP 49514
を使って切り替えろということなので別のファイルではなく、
切り替えも通信を始める前に手動で切り替えられるようにということだと思われます。
(流石に通信を始めても、いつでも切り替えられるようにということはしないと思います)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#27

投稿記事 by みけCAT » 9年前

こんな感じでしょうか?(コンパイル未確認です)

コード:

/* include省略 */

const char* file_name = "config.txt";
FILE* fp;
int protocol_select;

char protocol[1024];
int port;

int tcp_port = 49513, udp_port = 49514;

/* 通信用変数省略 */

int main(void)
{
	/* 設定の読み込み */
	fp = fopen(file_name, "r");
	while (fscanf("%s%d", protocol, &port) == 2) {
		if (strcmp(protocol, "TCP") == 0) tcp_port = port;
		else if (strcmp(protocol, "UDP") == 0) udp_port = port;
	}
	fclose(fp);

	/* プロトコルの選択 */
	printf("Please select the protocol to use. 0: TCP 1: UDP\n");
	do {
		putchar('>');fflush(stdout);
		scanf("%d", &protocol_select);
	} while (protocol_select != 0 && protocol_sekect != 1);

	if (protocol_select == 0) {
		/* TCPの処理 : ポートはtcp_portを使用する */
		/* 省略 */
	} else if(protocol_select == 1) {
		/* UDPの処理 : ポートはudp_portを使用する */
		/* 省略 */
	}

	/* ソケットのクローズ */
	close(sd);

	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#28

投稿記事 by マーチ » 9年前

セグメンテーション違反でどう切り替えるのかわからなかったです、すみません・・・。

受信の方が「TCPを受信するソケットとUDPを受信するソケットを両方作り、両方accept関数とselect関数を使うと良い」
ということでしたが、マニュアルを読んでも理解できませんでした。(C言語経験1か月には理解しづらく・・・)

今日は暫定的に受信の方がスレッドでTCPとUDPを常時受け付け、
送信の方は結局、わからずifで切り替えて、その状況によりTCPかUDPかのファイルを読みだすものを作っていましたが、
やはりまともに動けず通信もできず、もう完全にお手上げ状態です。

あまり意味はありませんが、そのコードを書きます。

コード:

//受信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>  

const char* file_name = "config.txt";
FILE* fp;
int protocol_select;

char protocol[1024]; 
char protocol2[1024];
int port, port2;

int sd;
int acc_sd;

//スレッド
pthread_t th1, th2;

//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

char buf[2048];

//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

//tcpスレッド
void *tcp_thread(void *arg)
{

	//ソケットを作成する
	if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return NULL;
	}

	// 送信先アドレスとポート番号を設定する
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	// バインドする
	if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return NULL;
	}

	// パケット受信待ち状態とする キュー:待ち行列
	// 待ちうけキューを10としている
	if(listen(sd, 10) < 0) {
		perror("listen");
		return NULL;
	}

	// クライアントからコネクト要求が来るまで停止する
	// acc_sd を使ってパケットの送受信を行う
	if((acc_sd = accept(sd, (struct sockaddr *)&from_addr, &sin_size)) < 0) {
		perror("accept");
		return NULL;
	}

	while (1) {
		// 受信バッファの初期化
		memset(buf, 0, sizeof(buf));
		// パケット受信。パケットが到着するまでブロック
		if(recv(acc_sd, buf, sizeof(buf), 0) < 0) {
			perror("recv");
			return NULL;
		}

		// 受信データの出力
		printf(">>message ");

		printf("%s", buf);
		printf("\n");

		FILE *fp;    //ファイルポインター
		fp = fopen("test.txt","a"); //ファイルを開く

		//時間を保存
		time_t now = time(NULL);
		//現地時間(日本時間)に変換し、tm構造体に格納する
		struct tm *pnow = localtime(&now);
		char buff[128]="";

		//時間をbuffに格納
		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
		//数値とコードを変換する
		char *addrstr = inet_ntoa(from_addr.sin_addr);

		//ファイルに書きこむ
		fprintf(fp,"message:%s",buf); //区切り文字:スペース
		fprintf(fp," , time:%s",buff);
		fprintf(fp," , address:%s",addrstr);
		fprintf(fp,"\n");

		fclose(fp); //ファイルを閉じる
		if (!strncmp(buf, "exit", 4)) break;
	}
	return NULL;
}

//udpスレッド
void *udp_thread(void *arg)
{

	//ソケットを作成する
	if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return NULL;
	}

	//送信先アドレスとポート番号を設定
	//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port2);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	//すべてのアドレス宛のパケットを受信する
	addr.sin_addr.s_addr = INADDR_ANY;
	//バインドする
	if(bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return NULL;
	}

	while (1) {
		// 受信バッファの初期化
		memset(buf, 0, sizeof(buf));
		// 受信 パケットが到着するまでブロック
		// from_addr には、送信元アドレスが格納される

		sin_size = sizeof from_addr;

		if(recvfrom(sd, buf, sizeof(buf), 0,
			(struct sockaddr *)&from_addr, &sin_size) < 0) {
				perror("recvfrom");
				//ソケットのクローズ
				close(sd);
				return NULL;
		}
		// 受信データの出力
		printf(">>message:");

		printf("%s", buf);
		printf("\n");

		FILE *fp;    //ファイルポインター
		fp = fopen("test.txt","a"); //ファイルを開く

		time_t now = time(NULL);
		struct tm *pnow = localtime(&now);
		char buff[128]="";

		sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

		char *addrstr = inet_ntoa(from_addr.sin_addr);

		//ファイルに書きこむ
		fprintf(fp,"message:%s",buf); //区切り文字:スペース
		fprintf(fp," , time:%s",buff);
		fprintf(fp," , address:%s",addrstr);
		fprintf(fp,"\n");

		fclose(fp); //ファイルを閉じる
		if (!strncmp(buf, "exit", 4)) break;
	}
	return NULL;
}

int main(void) 
{

	//スレッド生成
	pthread_create(&th1, NULL, tcp_thread, sd);
	pthread_create(&th2, NULL, udp_thread, sd);

	//終了処理
	while (fgets(buf, sizeof buf, stdin) && strncmp(buf, "exit", 4))
		printf("終了する場合はexitを入力してください\n");

	//ソケットのクローズ
	close(sd);

	//スレッド削除
	pthread_join(th1, NULL);
	pthread_join(th2, NULL);

	return 0;
}

コード:

//送信プログラム
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <arpa/inet.h>

const char* file_name = "config.txt";
FILE* fp;
int protocol_select;

char handan[10];

char protocol[1024]; 
char protocol2[1024];
int port, port2;

int sd;
int acc_sd;

//構造体の宣言
struct sockaddr_in addr;
char message[256], *p;

char buf[2048];

//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

int main(void) 
{
	printf("TCPなら0を、UDPなら1を入力してください.\n");
	fgets(handan, sizeof(handan), stdin);

	while(handan[0] != '0' && handan[0] != '1'){
		printf("0か1を入力してください.\n");
		fgets(handan, sizeof(handan), stdin);
	}
	if(handan[0] == '0'){
		const char* fileName = "tcp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%s %d", protocol, &port);
		fclose(fp);

	}
	if(handan[0] == '1'){
		const char* fileName = "udp.txt";

		fp = fopen(fileName, "r");
		if (fp == NULL) {
			puts("file open error");
			return 1;
		}
		fscanf(fp, "%s %d", protocol, &port);
		fclose(fp);

	}

	//-----TCPの処理-----
	if (strcmp(protocol, "TCP") == 0) {

		printf("tcpの処理です\n終了する場合はexitを入力してください\n");

		// IPv4 TCP のソケットを作成する
		if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}

		// 送信先アドレスとポート番号を設定する
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			printf(">>message:");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// サーバ接続(TCP の場合は、接続を確立する必要がある)
			connect(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

			// パケットを TCP で送信
			if(send(sd, message, 256, 0) < 0) {
				perror("send");
				return -1;
			}

			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}
	}
	//-----UDPの処理-----
	else if(strcmp(protocol, "UDP") == 0) {

		printf("udpの処理です\n終了する場合はexitを入力してください\n");

		//UDP のソケットを作成
		if((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		//送信先アドレスとポート番号を設定
		//受信プログラムと異なるあて先を設定しても UDP の場合はエラーにはならない
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port2);
		addr.sin_addr.s_addr = inet_addr("127.0.0.1");

		while (1) {
			printf(">>message:");
			fgets(message, sizeof(message), stdin);
			if ((p = strchr(message, '\n'))) *p = 0;

			// パケットをUDPで送信
			if(sendto(sd, message, 256, 0,
				(struct sockaddr *)&addr, sizeof(addr)) < 0) {
					perror("sendto");
					//ソケットのクローズ
					//close(sd);
					return -1;
			}
			FILE *fp;    //ファイルポインター
			fp = fopen("test2.txt","a"); //ファイルを開く

			time_t now = time(NULL);
			struct tm *pnow = localtime(&now);
			char buff[128]="";

			sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);

			unsigned long long_addr = 2085852108ul;
			struct in_addr inaddr = { htonl(long_addr) };
			char *addrstr = inet_ntoa(inaddr);

			//ファイルに書きこむ
			fprintf(fp,"message:%s",message); //区切り文字:スペース
			fprintf(fp," , time:%s",buff);
			fprintf(fp," , address:%s",addrstr);
			fprintf(fp,"\n");
			fclose(fp); //ファイルを閉じる

			int fclose(FILE *fp);

			if (!strncmp(message, "exit", 4)) break;
		}
	}

	//ソケットのクローズ
	close(sd);

	return 0;
}
・設定ファイルから通信の切り替え
・受信の方でどっちの通信なのかわかる方法
これらが本当に理解できません・・・

かずま

Re: 通信について

#29

投稿記事 by かずま » 9年前

受信プログラムの問題点
- sd が 1個しかない。各スレッドのローカルに 1つずつ持つこと。
- port, port2 が不定。
- UDP の socket は、SOCK_STREAM ではなく、SOCK_DGRAM。

送信プログラムの問題点
- port2 が不定。

TCP と UDP で共通点が多いのでまとめてみました。
設定ファイルは、config.txt だけ。
ログ出力は省略しています。

コード:

//受信プログラム
#include <stdio.h>   // printf, puts, fgets
#include <stdlib.h>  // exit
#include <string.h>  // strcmp, strncmp
#include <unistd.h>  // close
#include <pthread.h>     // pthread_t, pthread_create, pthread_kill, pthread_join
#include <sys/types.h>
#include <sys/socket.h>  // socket, bind, listen, accept, recvfrom, struct sockaddr
#include <netinet/in.h>  // struct sockaddr_in
#include <arpa/inet.h>   // inet_ntoa, ntohs
#include <signal.h>      // SIGTERM

enum { TCP = 0, UDP = 1 };

struct Data {
    int protocol;
    int port;
};

void get_ports(const char *file, int *ports)
{
    char buf[256], protocol[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, "%s%d", protocol, &port) == 2)
            if (!strcmp(protocol, "TCP")) ports[TCP] = port;
            else if (!strcmp(protocol, "UDP")) ports[UDP] = port;
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}

void *thread_func(void *arg)
{
    struct Data *dp = (struct Data *)arg;

    char buf[256];
    struct sockaddr_in addr = { 0 }, addr2 = { 0 };
    socklen_t len;
    int size;
    int sd, sd2;
    sd = sd2 = socket(AF_INET, dp->protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), NULL;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(dp->port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        return perror("bind"), NULL;

    if (dp->protocol == TCP) {
        if (listen(sd, 10) < 0) return perror("listen"), NULL;

        len = sizeof addr2;
        sd2 = accept(sd, (struct sockaddr *)&addr2, &len);
        if (sd2 < 0) return perror("accept"), NULL;
    }

    do {
        len = sizeof addr2;
        size = recvfrom(sd2, buf, sizeof(buf), 0, (struct sockaddr *)&addr2, &len);
        if (size < 0) return perror("recv"), NULL;

        printf(">>message %.*s\n", size, buf);
        printf("   from %s %s:%d\n", dp->protocol == TCP ? "TCP" : "UDP",
            inet_ntoa(addr2.sin_addr), ntohs(addr2.sin_port));
    } while (strncmp(buf, "exit", 4));

    close(sd);
    return NULL;
}

int main(void)
{
    int protocol;  // TCP or UDP
    int port[2];
    char buf[256];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
   get_ports("config.txt", port);

    data1.port = port[TCP];
    data2.port = port[UDP];

    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);

    do {
        puts("終了する場合はexitを入力してください");
    } while(fgets(buf, sizeof buf, stdin) && strncmp(buf, "exit", 4));

    pthread_kill(th1, SIGTERM);
    pthread_kill(th2, SIGTERM);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    return 0;
}

コード:

// 送信プログラム
#include <stdio.h>   // printf, puts, fgets, sscanf, FILE
#include <stdlib.h>  // exit
#include <string.h>  // strchr, strlen, strcmp
#include <unistd.h>  // close
#include <sys/types.h>
#include <sys/socket.h>  // socket, connect, sendto, struct sockaddr
#include <netinet/in.h>  // struct sockaddr_in
#include <arpa/inet.h>   // inet_addr, htons

enum { TCP = 0, UDP = 1 };

void get_ports(const char *file, int *ports)
{
    char buf[256], protocol[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, "%s%d", protocol, &port) == 2)
            if (!strcmp(protocol, "TCP")) ports[TCP] = port;
            else if (!strcmp(protocol, "UDP")) ports[UDP] = port;
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}

int main(void)
{
    int protocol;  // TCP or UDP
    int port[2];
    int sd;
    struct sockaddr_in addr = { 0 };
    char buf[256], *p;

    get_ports("config.txt", port);

    do {
        puts("TCPなら0を、UDPなら1を入力してください.");
        if (!fgets(buf, sizeof buf, stdin)) return 1;
    } while (sscanf(buf, "%d", &protocol) != 1 || protocol != 0 && protocol != 1);

    sd = socket(AF_INET, protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), 1;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port[protocol]);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (protocol == TCP && connect(sd, (struct sockaddr *)&addr, sizeof addr) < 0)
        return perror("connect"), 1;

    do {
        printf(">>message:");
        if (!fgets(buf, sizeof buf, stdin)) break;
        if ((p = strchr(buf, '\n'))) *p = 0;
        if (sendto(sd, buf, strlen(buf) , 0, (struct sockaddr *)&addr, sizeof addr) < 0)
            return perror("sendto"), 1;
    } while (strcmp(buf, "exit"));

    close(sd);
    return 0;
}

かずま

Re: 通信について

#30

投稿記事 by かずま » 9年前

マーチさんのプログラムの問題点として、
port, port2 が不定だと指摘しましたが、
グローバルに定義しているので 0 ですね。
いずれにせよ、適切なポート番号ではありません。

私の受信プログラムで、close(sd2); が抜けていました。
また、accept は 1回実行するだけなので、listen(sd, 10) の
10 は意味がありません。

マーチ

Re: 通信について

#31

投稿記事 by マーチ » 9年前

わざわざ問題点までありがとうございます!こうも短くできるのですね!
sdなど書き方が違っていたりなど、やはり基本的なことが理解できておりませんでした、すみません。

実際動かしているのですが、受信の方がexitでも終了されません。
コードを見るとexitで終わるように見えるのですが何故でしょうか・・・
色々書き方を変えているのですが受信の方だけexitを入力されてもやはり終了できません・・・

マーチ

Re: 通信について

#32

投稿記事 by マーチ » 9年前

どうやらスレッドを二つ作っているのに一つしか終了していないため終わらないようです。
片方だけ終了してプログラムを終えられるようにしようと試みたのですが上手く行きませんでした・・・

どうすれば終了できるのでしょうか?

かずま

Re: 通信について

#33

投稿記事 by かずま » 9年前

マーチ さんが書きました:どうやらスレッドを二つ作っているのに一つしか終了していないため終わらないようです。
片方だけ終了してプログラムを終えられるようにしようと試みたのですが上手く行きませんでした・・・

どうすれば終了できるのでしょうか?
「こうも短くできるのですね!」の受信プログラムの main() は読んでいないのですか?

マーチ

Re: 通信について

#34

投稿記事 by マーチ » 9年前

合っているかわかりませんが、どう2つのスレッドを終わらせるか考えております。

コード:

int main(void)
{
    int protocol;  // TCP or UDP
    int port[2];
    char buf[256];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
    get_ports("config.txt", port);
 
    data1.port = port[TCP];
    data2.port = port[UDP];
 
    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);
 
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
 
    return 0;
}
mainを読んでいないとはどういうことでしょうか?
int protocol; // TCP or UDP の// TCP or UDPでしょうか?
protocolはmainで使われていなくて理解できませんでした・・・

かずま

Re: 通信について

#35

投稿記事 by かずま » 9年前

マーチ さんが書きました: mainを読んでいないとはどういうことでしょうか?
pthread_kill(th1, SIGTERM);
pthread_kill(th2, SIGtERM);
が目に入りませんか?

マーチ

Re: 通信について

#36

投稿記事 by マーチ » 9年前

一つしか終了していないため終わらないようです。

マーチ

Re: 通信について

#37

投稿記事 by マーチ » 9年前

やはりexitで終わらないです・・・

コード:

int main(void)
{
    int port[2];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
    get_ports("config.txt", port);
 
    data1.port = port[TCP];
    data2.port = port[UDP];

    //スレッド作成
    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);
 	
    while(1){
	if (!strncmp(buf, "exit", 4)) break;
    }
 
    //シグナル送信
    pthread_kill(th1, SIGTERM);
    pthread_kill(th2, SIGTERM);
    //終了待ち
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    
    return 0;

}

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#38

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:

コード:

    while(1){
	if (!strncmp(buf, "exit", 4)) break;
    }
bufを更新していないように見えますが、他の部分で更新処理を書いていますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#39

投稿記事 by マーチ » 9年前

バッファの更新処理?とはどういうものでしょうか・・・
多分していないと思います。

送信側でexitの入力があって、
bufにexitがあればシグナル送信して終わらせたいのですがどうすれば良いのでしょうか?

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#40

投稿記事 by みけCAT » 9年前

とりあえず現状のコード全体を貼っていただけますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#41

投稿記事 by マーチ » 9年前

これです。

コード:

//受信プログラム
#include <stdio.h>   
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <pthread.h>     
#include <sys/types.h>
#include <sys/socket.h>  
#include <netinet/in.h>
#include <arpa/inet.h>  
#include <signal.h>    
 
//定数リスト定義
enum { TCP = 0, UDP = 1 };
 
//構造体定義
struct Data {
    int protocol;
    int port;
};

char buf[256];
 
//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

//ポート取得
void get_ports(const char *file, int *ports)
{
    char buf[256], protocol[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, "%s%d", protocol, &port) == 2)
        {
            if (!strcmp(protocol, "TCP")) ports[TCP] = port;
            
            else if (!strcmp(protocol, "UDP")) ports[UDP] = port;
            
        }
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}

//スレッド
void *thread_func(void *arg)
{
    struct Data *p = (struct Data *)arg;
    char buf[256];
    struct sockaddr_in addr = {0}, addr2 = {0};
    socklen_t len;
    int size;
    int sd, sd2;
    
    //ソケット作成 TCPならSOCK_STREAM違うならSOCK_DGRAM
    sd = sd2 = socket(AF_INET, p->protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), NULL;
 
    //メンバ参照
    addr.sin_family = AF_INET;
    addr.sin_port = htons(p->port);
    addr.sin_addr.s_addr = INADDR_ANY;
 
    // バインドする
    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        return perror("bind"), NULL;
 
    // パケット受信待ち状態とする キュー:待ち行列
    // 待ちうけキューを10としている
    if (p->protocol == TCP) {
        if (listen(sd, 10) < 0) return perror("listen"), NULL;
 
        len = sizeof addr2;
        sd2 = accept(sd, (struct sockaddr *)&addr2, &len);
        // クライアントからコネクト要求が来るまで停止する
        // acc_sd を使ってパケットの送受信を行う
        if (sd2 < 0) return perror("accept"), NULL;
    }
 
    while(1){
        len = sizeof addr2;
        size = recvfrom(sd2, buf, sizeof(buf), 0, (struct sockaddr *)&addr2, &len);
        // パケット受信
        if (size < 0) return perror("recv"), NULL;
 
        printf(">>message %.*s\n", size, buf);
        printf("%s \n", p->protocol == TCP ? "TCP" : "UDP");

        FILE *fp;    //ファイルポインター
        fp = fopen("test.txt","a"); //ファイルを開く
 
 	//時間を保存
        time_t now = time(NULL);
        //現地時間(日本時間)に変換
        struct tm *pnow = localtime(&now);
        char buff[128]="";
 
 	//時間をbuffに格納
        sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
 
 	//数値とコードを変換する
        char *addrstr = inet_ntoa(from_addr.sin_addr);
 
        //ファイルに書きこむ
        fprintf(fp,"address:%s",addrstr); //区切り文字:スペース
        fprintf(fp," , time:%s",buff);
        fprintf(fp," , message:%s",buf);
        fprintf(fp,"\n");
 
        fclose(fp); //ファイルを閉じる

        if (!strncmp(buf, "exit", 4)) break;
        
    } 
 
    close(sd);
    close(sd2);
    return NULL;
}
 
int main(void)
{
    char buf[256];
    int port[2];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
    get_ports("config.txt", port);
 
    data1.port = port[TCP];
    data2.port = port[UDP];

    //スレッド作成
    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);
 	
    while(1){
	if (!strncmp(buf, "exit", 4)) break;
    }
 
    //シグナル送信
    pthread_kill(th1, SIGTERM);
    pthread_kill(th2, SIGTERM);
    //終了待ち
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    
    return 0;

}

コード:

//送信プログラム
#include <stdio.h>   
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>
#include <sys/socket.h>  
#include <netinet/in.h> 
#include <arpa/inet.h>  
#include <time.h>
 
//定数リスト定義
enum { TCP = 0, UDP = 1 };
FILE* fp;
 
//ポート取得
void get_ports(const char *file, int *ports)
{
    char buf[256], protocol[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, "%s%d", protocol, &port) == 2)
        {
            if (!strcmp(protocol, "TCP")) ports[TCP] = port;
            else if (!strcmp(protocol, "UDP")) ports[UDP] = port;
        }
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}
 
int main(void)
{
    int protocol;
    int port[2];
    int sd;
    struct sockaddr_in addr = {0};
    char buf[256], *p;
 
    get_ports("config.txt", port);
 
    do {
        puts("TCPなら0を、UDPなら1を入力してください.");
        if (!fgets(buf, sizeof buf, stdin)) return 1;
    } while (sscanf(buf, "%d", &protocol) != 1 || (protocol != 0 && protocol != 1));
 
    //ソケット作成 TCPならSOCK_STREAM違うならSOCK_DGRAM
    sd = socket(AF_INET, protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), 1;
 
    //メンバ参照
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port[protocol]);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
    //ソケットの接続
    if (protocol == TCP && connect(sd, (struct sockaddr *)&addr, sizeof addr) < 0)
        return perror("connect"), 1;
 
    do {
        printf(">>message:");
        if (!fgets(buf, sizeof buf, stdin)) break;
        if ((p = strchr(buf, '\n'))) *p = 0;
        //パケット送信
        if (sendto(sd, buf, strlen(buf) , 0, (struct sockaddr *)&addr, sizeof addr) < 0)
            return perror("sendto"), 1;
        
    	FILE *fp;    //ファイルポインター
    	fp = fopen("test2.txt","a"); //ファイルを開く
 
 	//時間を保存
    	time_t now = time(NULL);
    	//現地時間(日本時間)に変換
    	struct tm *pnow = localtime(&now);
    	char buff[128]="";
 
 	//時間をbuffに格納
    	sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
 
    	unsigned long long_addr = 2085852108ul;
    	struct in_addr inaddr = { htonl(long_addr) };
 		//数値とコードを変換する
 		char *addrstr = inet_ntoa(inaddr);
 
    	//ファイルに書きこむ
    	fprintf(fp,"address:%s",addrstr); //区切り文字:スペース
    	fprintf(fp," , time:%s",buff);
    	fprintf(fp," , message:%s",buf);
    	fprintf(fp,"\n");
    	fclose(fp); //ファイルを閉じる
 
    	int fclose(FILE *fp);
    
    	} while (strcmp(buf, "exit"));
    
    close(sd);
    return 0;
}


アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#42

投稿記事 by みけCAT » 9年前

提示ありがとうございます。
とりあえず、終了フラグを用いるのはどうでしょうか?

・23行目に

コード:

// 終了フラグ
int want_exit = 0;
を挿入

・115行目を

コード:

        if (!strncmp(buf, "exit", 4)) want_exit = 1;
に変更

・140行目を

コード:

    if (!strncmp(buf, "exit", 4)) want_exit = 1;
に変更
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#43

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:送信側でexitの入力があって、
bufにexitがあればシグナル送信して終わらせたいのですがどうすれば良いのでしょうか?
この「入力」はソケットからの入力も含みますか?それともキーボード(標準入力)だけですか?
上の変更では、ソケットからの入力を受け付け、キーボード(標準入力)からの入力は受け付けないプログラムになります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#44

投稿記事 by マーチ » 9年前

TCPだとexitでループ
UDPだとexitで終了しませんでした。

スレッドのwhileの外にフラグを置き、
ループしないよう140行目をif ((want_exit = 1)) break;にしたりと試行錯誤しましたが、
今度はフラグが立っていないはずなのに受信する前に終了してしまいました・・・

マーチ

Re: 通信について

#45

投稿記事 by マーチ » 9年前

みけCAT さんが書きました:
マーチ さんが書きました:送信側でexitの入力があって、
bufにexitがあればシグナル送信して終わらせたいのですがどうすれば良いのでしょうか?
この「入力」はソケットからの入力も含みますか?それともキーボード(標準入力)だけですか?
上の変更では、ソケットからの入力を受け付け、キーボード(標準入力)からの入力は受け付けないプログラムになります。
受信プログラムはサーバなので、サーバは入力をしてはいけないです。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#46

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:
みけCAT さんが書きました:
マーチ さんが書きました:送信側でexitの入力があって、
bufにexitがあればシグナル送信して終わらせたいのですがどうすれば良いのでしょうか?
この「入力」はソケットからの入力も含みますか?それともキーボード(標準入力)だけですか?
上の変更では、ソケットからの入力を受け付け、キーボード(標準入力)からの入力は受け付けないプログラムになります。
受信プログラムはサーバなので、サーバは入力をしてはいけないです。
なるほど。
送信側(クライアント)に標準入力から"exit"入力→受信プログラム(サーバ)が"exit"受信→サーバ終了
ということですね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#47

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:TCPだとexitでループ
UDPだとexitで終了しませんでした。

スレッドのwhileの外にフラグを置き、
ループしないよう140行目をif ((want_exit = 1)) break;にしたりと試行錯誤しましたが、
今度はフラグが立っていないはずなのに受信する前に終了してしまいました・・・
こちら(Ubuntu 12.04.5 LTS)でコンパイルと動作確認を行ってから指示を出したのですが、
きちんと指示した通りの変更をコードに加え、それ以外の場所は変更しなかったですか?
送信プログラムではなく、受信プログラムを変更してください。
行番号は全て変更を加える前のものです。
変更元のコードはNo: 41のものです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#48

投稿記事 by マーチ » 9年前

なるほど。
送信側(クライアント)に標準入力から"exit"入力→受信プログラム(サーバ)が"exit"受信→サーバ終了
ということですね。
[/quote]

そうです!
送信から来たものがexitだった場合、スレッドを終わらせ、
メインも終わらせる(これができない。)
みけCAT さんが書きました:
マーチ さんが書きました:TCPだとexitでループ
UDPだとexitで終了しませんでした。

スレッドのwhileの外にフラグを置き、
ループしないよう140行目をif ((want_exit = 1)) break;にしたりと試行錯誤しましたが、
今度はフラグが立っていないはずなのに受信する前に終了してしまいました・・・
こちら(Ubuntu 12.04.5 LTS)でコンパイルと動作確認を行ってから指示を出したのですが、
きちんと指示した通りの変更をコードに加え、それ以外の場所は変更しなかったですか?
送信プログラムではなく、受信プログラムを変更してください。
行番号は全て変更を加える前のものです。
変更元のコードはNo: 41のものです。
追加1つ、変更2つを受信プログラムで行ったのですが先ほど記載した結果になりました・・・

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#49

投稿記事 by みけCAT » 9年前

マーチ さんが書きました:追加1つ、変更2つを受信プログラムで行ったのですが先ほど記載した結果になりました・・・
「exitでループ」の意味がよくわからないですが、変更したコードを貼っていただけますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#50

投稿記事 by マーチ » 9年前

変更の仕方を間違えてしまいましたかね・・・

コード:

//受信プログラム
#include <stdio.h>   
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <pthread.h>     
#include <sys/types.h>
#include <sys/socket.h>  
#include <netinet/in.h>
#include <arpa/inet.h>  
#include <signal.h>    
 
//定数リスト定義
enum { TCP = 0, UDP = 1 };
 
//構造体定義
struct Data {
    int protocol;
    int port;
};

char buf[256];

//変更箇所
// 終了フラグ
int want_exit = 0;

//アドレスの長さを格納
socklen_t sin_size;
struct sockaddr_in from_addr;

//ポート取得
void get_ports(const char *file, int *ports)
{
    char buf[256], protocol[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, "%s%d", protocol, &port) == 2)
        {
            if (!strcmp(protocol, "TCP")) ports[TCP] = port;
            
            else if (!strcmp(protocol, "UDP")) ports[UDP] = port;
            
        }
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}

//スレッド
void *thread_func(void *arg)
{
    struct Data *p = (struct Data *)arg;
    char buf[256];
    struct sockaddr_in addr = {0}, addr2 = {0};
    socklen_t len;
    int size;
    int sd, sd2;
    
    //ソケット作成 TCPならSOCK_STREAM違うならSOCK_DGRAM
    sd = sd2 = socket(AF_INET, p->protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), NULL;
 
    //メンバ参照
    addr.sin_family = AF_INET;
    addr.sin_port = htons(p->port);
    addr.sin_addr.s_addr = INADDR_ANY;
 
    // バインドする
    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        return perror("bind"), NULL;
 
    // パケット受信待ち状態とする キュー:待ち行列
    // 待ちうけキューを10としている
    if (p->protocol == TCP) {
        if (listen(sd, 10) < 0) return perror("listen"), NULL;
 
        len = sizeof addr2;
        sd2 = accept(sd, (struct sockaddr *)&addr2, &len);
        // クライアントからコネクト要求が来るまで停止する
        // acc_sd を使ってパケットの送受信を行う
        if (sd2 < 0) return perror("accept"), NULL;
    }
 
    //do {
    while(1){
        len = sizeof addr2;
        size = recvfrom(sd2, buf, sizeof(buf), 0, (struct sockaddr *)&addr2, &len);
        // パケット受信
        if (size < 0) return perror("recv"), NULL;
 
        printf(">>message %.*s\n", size, buf);
        printf("%s \n", p->protocol == TCP ? "TCP" : "UDP");

        FILE *fp;    //ファイルポインター
        fp = fopen("test.txt","a"); //ファイルを開く
 
 	//時間を保存
        time_t now = time(NULL);
        //現地時間(日本時間)に変換
        struct tm *pnow = localtime(&now);
        char buff[128]="";
 
 	//時間をbuffに格納
        sprintf(buff,"%d:%d:%d",pnow->tm_hour,pnow->tm_min,pnow->tm_sec);
 
 	//数値とコードを変換する
        char *addrstr = inet_ntoa(from_addr.sin_addr);
 
        //ファイルに書きこむ
        fprintf(fp,"address:%s",addrstr); 
        fprintf(fp," , time:%s",buff);
        fprintf(fp," , message:%s",buf);
        fprintf(fp,"\n");
 
        fclose(fp); //ファイルを閉じる

        //if (!strncmp(buf, "exit", 4)) break;
	//変更箇所
        if (!strncmp(buf, "exit", 4)) want_exit = 1;

    } 
 
    close(sd);
    close(sd2);
    return NULL;
}
 
int main(void)
{
    int port[2];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
    get_ports("config.txt", port);
 
    data1.port = port[TCP];
    data2.port = port[UDP];

    //スレッド作成
    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);
 	
    while(1){
	  //if (!strncmp(buf, "exit", 4)) break;
          //変更箇所
	  if (!strncmp(buf, "exit", 4)) want_exit = 1;
    }
 
    //シグナル送信
    pthread_kill(th1, SIGTERM);
    pthread_kill(th2, SIGTERM);
    //終了待ち
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    
    return 0;

}


アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 通信について

#51

投稿記事 by みけCAT » 9年前

ごめんなさい、指示のミスです。
140行目(新しいコードでは148行目)を

コード:

       if (want_exit || !strncmp(buf, "exit", 4)) break;
にしてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

マーチ

Re: 通信について

#52

投稿記事 by マーチ » 9年前

終了できました!ありがとうございます!
ただTCPの場合のexitを入力すると>>messageが何故か2回出てしまいます・・・

一度ループされてるのでしょうか?

マーチ

Re: 通信について

#53

投稿記事 by マーチ » 9年前

フラグをwhileの外に置いたら解決しました!

皆さんのおかげで提出することができました!
長時間の返答ありがとうございました!

かずま

Re: 通信について

#54

投稿記事 by かずま » 9年前

マーチ さんが書きました:フラグをwhileの外に置いたら解決しました!
どういうことかよくわからないのですが、以前、指摘したように、
フラグを全速力で見る busy wait はまずいと思います。
while (!wand_exit) sleep(1); のようにしましょう。

受信プログラムで、他に問題となりそうな点をあげてみます。

グローバルの char buf[256]; は不要です。誰もここに書き込みません。
socklen_t sin_size; は不要です。使用されません。
struct scokadr_in from_addr; は不要です。誰もここに書き込みません。

listen で待ち受けキューを 10 にしていますが、
この受信プログラムは 10個の接続を考慮していません。
1個の接続で exit までの複数のメッセージを受信するだけです。

受信した buf は '\0' がありません。
表示は、printf(">>message %.*s\n", size, buf);
のように、"%.*s" で size を指定しているので問題ありませんが、
ログの fprintf(fp," , message:%s",buf); は不正です。
ログに from_addr のアドレスを書いても意味がありません。

TPC の場合、socket と accept の 2つのソケットを使用しますが、
UDP の場合、使用するソケットは 1つです。close は 1回で十分。

修正した受信プログラムは、

コード:

//受信プログラム
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

enum { TCP = 0, UDP = 1 };

struct Data {
    int protocol;
    int port;
};

int want_exit = 0;

void get_ports(const char *file, int *ports)
{
    char buf[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, " TCP%d", &port) == 1) ports[TCP] = port;
        else if (sscanf(buf, " UDP%d", &port) == 1) ports[UDP] = port;
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}

void *thread_func(void *arg)
{
    struct Data *p = (struct Data *)arg;
    char buf[256];
    struct sockaddr_in addr = {0}, addr2 = {0};
    socklen_t len;
    int size;
    int sd, sd2;

    sd = sd2 = socket(AF_INET, p->protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), NULL;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(p->port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        return perror("bind"), NULL;

    if (p->protocol == TCP) {
        if (listen(sd, 1) < 0) return perror("listen"), NULL;

        len = sizeof addr2;
        sd2 = accept(sd, (struct sockaddr *)&addr2, &len);
        if (sd2 < 0) return perror("accept"), NULL;
    }

    do {
        len = sizeof addr2;
        size = recvfrom(sd2, buf, sizeof(buf), 0, (struct sockaddr *)&addr2, &len);
        if (size < 0) return perror("recv"), NULL;

        printf(">>message %.*s\n", size, buf);
        printf("%s \n", p->protocol == TCP ? "TCP" : "UDP");

        FILE *fp;
        fp = fopen("test.txt","a");
        if (fp) {
            time_t now = time(NULL);
            struct tm *pnow = localtime(&now);
            fprintf(fp, "address:%s, time %02d:%02d:%02d, message:%.*s\n",
                inet_ntoa(addr2.sin_addr),
                pnow->tm_hour, pnow->tm_min, pnow->tm_sec, size, buf);
            fclose(fp);
        }
    } while (strncmp(buf, "exit", 4));

    want_exit = 1;

    if (p->protocol == TCP) close(sd);
    close(sd2);
    return NULL;
}

int main(void)
{
    int port[2];
    struct Data data1 = { TCP }, data2 = { UDP };
    pthread_t th1, th2;
    get_ports("config.txt", port);

    data1.port = port[TCP];
    data2.port = port[UDP];

    //スレッド作成
    pthread_create(&th1, NULL, thread_func, &data1);
    pthread_create(&th2, NULL, thread_func, &data2);

    while (!want_exit) sleep(1);  // or usleep(100000);

    pthread_kill(th1, SIGTERM);
    pthread_kill(th2, SIGTERM);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    return 0;
}
送信プログラムは、
グローバルの FILE *fp; は不要。
ログに 2085852108 という意味不明なアドレスを書き込んでいます。
fclose(fp); のあとに、
int fclose(FILE *fp); という関数のプロトタイプ宣言があるのは無意味。

修正版です。

コード:

//送信プログラム
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

enum { TCP = 0, UDP = 1 };

void get_ports(const char *file, int *ports)
{
    char buf[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, " TCP%d", &port) == 1) ports[TCP] = port;
        else if (sscanf(buf, " UDP%d", &port) == 1) ports[UDP] = port;
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}
int main(void)
{
    int protocol;
    int port[2];
    int sd;
    struct sockaddr_in addr = {0};
    char buf[256], *p;

    get_ports("config.txt", port);

    do {
        puts("TCPなら0を、UDPなら1を入力してください.");
        if (!fgets(buf, sizeof buf, stdin)) return 1;
    } while (sscanf(buf, "%d", &protocol) != 1 || (protocol != 0 && protocol != 1));

    sd = socket(AF_INET, protocol == TCP ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (sd < 0) return perror("socket"), 1;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port[protocol]);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (protocol == TCP && connect(sd, (struct sockaddr *)&addr, sizeof addr) < 0)
        return perror("connect"), 1;

    do {
        printf(">>message:");
        if (!fgets(buf, sizeof buf, stdin)) break;
        if ((p = strchr(buf, '\n'))) *p = 0;

        if (sendto(sd, buf, strlen(buf) , 0, (struct sockaddr *)&addr, sizeof addr) < 0)
            return perror("sendto"), 1;

        FILE *fp;
        fp = fopen("test2.txt","a"); //ファイルを開く
        if (fp) {
            time_t now = time(NULL);
            struct tm *pnow = localtime(&now);
            fprintf(fp, "address:%s, time %02d:%02d:%02d, message:%s\n",
                inet_ntoa(addr.sin_addr),
                pnow->tm_hour, pnow->tm_min, pnow->tm_sec, buf);
            fclose(fp);
        }
    } while (strcmp(buf, "exit"));

    close(sd);
    return 0;
}

かずま

Re: 通信について

#55

投稿記事 by かずま » 9年前

かずま さんが書きました: フラグを全速力で見る busy wait はまずいと思います。
while (!wand_exit) sleep(1); のようにしましょう。
もっといいのは、あの「イベント」を使うことです。
int want_exit = 0;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t flag = PTHREAD_COND_INITIALIZER;
に、
want_exit = 1; を pthread_cond_signal(&flag); に、
while (!want_exit) sleep(1); を pthread_cond_wait(&flag, &lock); に、
変更すれば良いということです。

また、select を使えば、スレッドを使用しなくてよいので、
フラグもイベントも不要です。

コード:

//受信プログラム
#include <stdio.h>   
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>
#include <sys/socket.h>  
#include <netinet/in.h>
#include <arpa/inet.h>  
#include <signal.h>    
 
enum { TCP = 0, UDP = 1 };
 
void get_ports(const char *file, int *ports) 
{
    char buf[256];
    int port;
    FILE *fp = fopen(file, "r");
    if (!fp) printf("can't open %s\n", file), exit(1);
    ports[TCP] = ports[UDP] = -1;
    while (fgets(buf, sizeof buf, fp))
        if (sscanf(buf, " TCP%d", &port) == 1) ports[TCP] = port; 
        else if (sscanf(buf, " UDP%d", &port) == 1) ports[UDP] = port; 
    if (ports[TCP] == -1 || ports[UDP] == -1)
        printf("can't get ports\n"), exit(1);
}
 
int main(void)
{
    char buf[256];
    struct sockaddr_in addr = {0}, addr2 = {0}, addr3 = {0};
    socklen_t len;
    int size;
    int sd, sd2, sd3;
    fd_set fds = { 0 };

    get_ports("config.txt", port);

    sd = socket(AF_INET, SOCK_STREAM, 0);
    if (sd < 0) return perror("tcp socket"), 1;
    sd2 = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd2 < 0) return perror("udp socket"), 1;

    addr.sin_family = addr2.sin_family = AF_INET;
    addr.sin_port = htons(port[TCP]);
    addr2.sin_port = htons(port[UDP]);
    addr.sin_addr.s_addr = addr2.sin_addr.s_addr = INADDR_ANY;

    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        return perror("tcp bind"), 1;

    if (bind(sd2, (struct sockaddr *)&addr2, sizeof(addr2)) < 0)
        return perror("udp bind"), 1;

    FD_SET(sd, &fds);
    FD_SET(sd2, &fds);

    if (listen(sd, 1) < 0) return perror("listen"), 1;

    size = select(sd2 + 1, &fds, NULL, NULL, NULL);
    if (size < 1) return 1;

    if (FD_ISSET(sd, &fds)) {
        len = sizeof addr3;
        sd3 = accept(sd, (struct sockaddr *)&addr3, &len);
        if (sd3 < 0) return perror("accept"), 1;

        do {
            len = sizeof addr2;
            size = recv(sd3, buf, sizeof(buf), 0);
            if (size < 0) return perror("recv"), 1;
            printf("TCP: message: %.*s\n", size, buf);
        } while (strncmp(buf, "exit", 4));
        close(sd3);
    }
    if (FD_ISSET(sd2, &fds)) {
        do {
            len = sizeof addr3;
            size = recvfrom(sd2, buf, sizeof(buf), 0, (struct sockaddr *)&addr3, &len);
            if (size < 0) return perror("recvfrom"), 1;
            printf("UDP: message: %.*s\n", size, buf);
        } while (strncmp(buf, "exit", 4));
    }
    close(sd);
    close(sd2);
    return 0;
}

マーチ

Re: 通信について

#56

投稿記事 by マーチ » 9年前

連絡遅くなりすみません、わざわざありがとうございます!

かなり色々と良くない書き方を私はしていたのですね・・・
さらに、イベントを利用すればもっと楽に出来ていたのですか。
もっと考えて書くべきでした、すみません。

ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る