• ベストアンサー

Socket通信プログラム

VisualC++6.0(SDK)を用いてSocket通信(UDP)プログラムを 作成(チャットやメッセンジャーのようなもの)しています。 送信用と受信用 1つのプログラムにて、送受信両方の機能を兼ね備えたものを作成しようと思っています。 で、送信側から送ったデータを受信側で処理(文字列の追加など)をして 受信側から送信側に送り戻したいとおもいます。 送信ボタンにより送信します。受信したデータはエディットボックスに表示します。 が、受信したデータを送り返すとしているために、 いつまでも、受信->送信->受信・・・・を繰り返してしまいます。 これを、正しく動作するようにするには、どのようにしたら良いのでしょうか? ご存知の方宜しくお願い致します。 足りないものがあったら、補足させていただきます。

質問者が選んだベストアンサー

  • ベストアンサー
  • onosuke
  • ベストアンサー率67% (310/456)
回答No.7

>ラッピング?? >似たような処理で覆い隠す?ということでしょうか。 大体そのような感じです。今回は「送信バッファへの操作を構造体への操作に置換できる」ような送信処理関数を考えています。 下半分の解説ですが,まず,前回のpacket_send()を2箇所訂正させてください。 BOOL packet_send(struct packet *p){ char buf[BUFSIZE]; #define p_type(p) ((u_char *)(p)) #define p_data(p) ( ((char *)p)+1 ) //訂正:型変換がまずかった。 *p_type(buf)=(u_char)p->type; //訂正:ポインタ参照の * が抜けていた。 strncpy(p_data(buf),p->data,DATASIZE); sendto(..., buf, BUFSIZE, ...); ... } このpacket_send()の動作は 1.構造体の内容を送信バッファへとコピーする。 2.作成した送信バッファをsendto()で送信する。 が目的です。(エラー処理は行っていない) その上で,質問のこの2文ですが, #define p_type(p) ((u_char *)(p)) #define p_data(p) ( ((char *)p)+1 ) //訂正:型変換がまずかった。 これは,送受信バッファ中で type, data, それぞれの位置(ポインタ)を取得するためのマクロです。目的は,送信/受信時にバッファの処理を共通化すること,後にプロトコルの改変を容易にすること,の2つ。関数に置き換えると //バッファ中のtypeを示すポインタを取得 u_char *p_type(void *p){ return (u_char *)(p);} //バッファ中のdataを示すポインタを取得 char *p_data(void *p){ return ( ((char *)p)+1 );} のようになります。 ## 引数にpじゃなく,bufを使ったほうがよかったかな?

coolguys
質問者

お礼

大変よく分かりました。 ポインタを取得していたんですか。 ありがとうございました。 少々話は変わるのですが、 ターゲットを見つける(接続が確立されている確認)というのも、 今までのように、返信データを用いて戻ってきたら、接続が確立されている という処理しかないのでしょうか? 繋がっている、繋がっていないということが、わかる方法はないのでしょうか? ご存知でしたら、教えていただけませんでしょうか。

その他の回答 (8)

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.9

UDPだとそうなりますね。 パケットタイプのフィールドを活用すると,いい感じにUDP pingを実装できそう。

coolguys
質問者

お礼

遅くなりましたが、 なんとか、なりました。 大変ありがとう御座いました。 また、質問したときは、よろしくお願いします。

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.8

UDP自体が接続(セッション)を確立しない通信ですから,無理です。 必要ならTCPを使いましょう。TCPなら,エラー処理でそういった状態を捕まえられます。

coolguys
質問者

お礼

回答ありがとうございます。 質問がどんどん、逆行しているようで申し訳ないです。 ということは、 試しと言うかで、1回送って返信作業をやって、 戻ってきたら、繋がっているということがある意味 接続確立テストということですね。

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.6

> 確か、「構造体の中を連続した領域で確保せよ!」という命令の仕方があった気がするのですが、 packed構造体ですね。 これは,C,C++言語で規定されていないので,それぞれのコンパイラに依存したコードが必要になります。 など偉そうなことをいいながら,私もVC++でのpacked構造体の使い方覚えていません。(^^;; VC++のヘルプを覗いてみてください。 私ならpacked構造体を使わず,このように送信処理をラッピングしてしますね。 #define BUFSIZE 100 #define DATASIZE (BUFSIZE-1) typedef enum packet_type{ PACKET_SEND=0x01, PACKET_ACK=0x02 } ptype_t; struct packet{ ptype_t type; char data[DATASIZE]; } BOOL packet_send(struct packet *p){ char buf[BUFSIZE]; #define p_type(p) ((u_char *)(p)) #define p_data(p) ((u_char *)(p+1)) p_type(buf)=(u_char)p->type; strncpy(p_data(buf),p->data,DATASIZE); sendto(..., buf, BUFSIZE, ...); ... }

coolguys
質問者

お礼

回答ありがとうございます。 packet構造体、調べてみようと思います。 ラッピング?? 似たような処理で覆い隠す?ということでしょうか。見た感じだと。 BOOL packet_send(struct packet *p){ char buf[BUFSIZE]; #define p_type(p) ((u_char *)(p)) #define p_data(p) ((u_char *)(p+1)) p_type(buf)=(u_char)p->type; strncpy(p_data(buf),p->data,DATASIZE); sendto(..., buf, BUFSIZE, ...); ... } 上半分は分かったのですが(単なる定義なので)、 下半分がいまいち理解できません。 特に #define p_type(p) ((u_char *)(p)) #define p_data(p) ((u_char *)(p+1)) の部分など。 もし宜しかったから、解説してはいただけないでしょうか?

  • honiyon
  • ベストアンサー率37% (331/872)
回答No.5

 こんにちは、honiyonです。   struct THoge {    char A[2]; //0~2の計3byte    char B[2]; //0~2の計3byte   }  と、THoge構造体を定義すると、メモリ上では、    AAABBB  と連続した 6byteの領域が確保される・・・と思いがちですが、実はそうではないのです。 THoge構造体のなかの、A, B各変数は全く別の場所に確保される事があります。  つまり、 THoge hoge; と宣言して、 @hoge として sendしても、中身を正常に送信出来ないという事になります。  確か、「構造体の中を連続した領域で確保せよ!」という命令の仕方があった気がするのですが、C/C++ではどうやってやるか忘れてしまいました(^^; 識者の方からの補足お待ちします(笑 お願いします(..  では、それ以外の方法での解決策としては、構造体の中身を手動で連続した領域に転送してやる事です。  例えば、  char Data[6]; //0~6 6byte.  と宣言して、  strcpy(@Data[0], hoge.A); strcpy(@Data[3], hoge.B);  とすれば、Dataの中には、 AAABBB\0 が入りますので、Dataの中の 0~5をsendすればOKです。 構造体の中に文字列以外が含まれる場合は、strcpyではなくmemcpyを使えば良いです。  今回のケースにおいては、構造体の内容は、送受信フラグとメッセージのみのようなので、↑のような面倒な事をするよりは、今行われている通り、「メッセージ中の最初の1byteをフラグに使用」という方がシンプルで良いかも知れません(笑  あまり良い回答ではないですが、参考になれば幸いです(.. 

coolguys
質問者

お礼

>構造体の中を連続した領域で確保せよ! やっぱりそういうのがあるんですね。 補足お待ちしております。 使いこなせるかどうかは別ですが・・・(^^j 構造体は連続した値ではないので、 手動にて、連続したデータを自分で作ってしまう。ナルホド。〆(。。) やっぱり、中身は手動でも、データ定義のときに 構造体だとかっこいいですよね。 大変参考になりました。 ありがとうございました。

  • honiyon
  • ベストアンサー率37% (331/872)
回答No.4

こんにちは、honiyonです。  送受信部についての具体例です。  送受信パケットは、Packet構造体に収められているとします。  「~ xxxx ~」は処理の省略です。 /*** 送信ボタンを押された時の処理 ***/  void OnSendButtonClick(){   ////// パケット作成処理   Packet.DataType = true; //初送信を示す。   ~その他メッセージ等をPacketに詰め込む処理~   ////// データ送信処理   ~パケットを相手に送信する~  }  /*** 受信を受けた時の処理 ***/  void OnRecvData(){   ~受信処理を行う~   ////// 初送信データなら返信する   if (Packet.DataType){    Packet.DataType = false; //not 初送信として循環防止!    ~返信処理~   ////// 返信されてきたデータならチェック   } else {    ~チェック処理~   }  }  こんな感じになると思います。  参考になれば幸いです(..

coolguys
質問者

お礼

honiyonさん、回答いただきありがとうございます。 バッチリ b(^^)d 動作できました。 でも、一つだけ分からないことがあったために、 もう一つだけ甘えさせてもらってもよいでしょうか? Packet構造体にデータとフラグを詰め込んだとしますよね。 struct Packet{   bool Type;   char Data[1000]; } とした場合に、送信時のsendtoにてキャストが出来ません。 sendtoの第2引数です。 で、今回は無理やりに文字列の1番目に力技にてフラグを入れたのですが、 構造体の方がスマートなやり方だと思っても出来ませんでした。 大変申し訳ありませんが、よろしくお願いできませんでしょうか?

  • honiyon
  • ベストアンサー率37% (331/872)
回答No.3

こんにちは、honiyonです。  そのような仕組みでしたら、送信データに「送信データか。返信データか」を見分けるチェック用フラグをもつ事で対応出来ると思います。  例えば、チェック用フラグを、送信ボタンで送信したデータはtrue, 返信するデータはfalseに設定し、受信側は、trueのデータであれば返信、falseならそのまま。とします。  既にこの仕組みは実践されているようですが、うまくいかないのであれば、それはバグであると思われます。再度コードをチェックしてみましょう。  因みに、データの整合性をチェックする場合、データを返送するという方法ではうまくいかないと思います。返信時にデータが壊れる可能性も考えられるからです。(UDPで壊れる可能性ってあまりないですけどね^^;)  データをチェックしたい場合は、一般に「パリティチェック」と呼ばれる手法が使われます。  簡単に言えば、データを2進数に直し、1の数を数えます。(0でもOK)その合計が、奇数であった場合は、1ビット足して、偶数にします。(その逆でもOK)  そして受信側で、同じように1の数を数え、必ず偶数になるようにそろえているのに、奇数になっていたら、どこかデータが壊れている!と判断できます。  壊れていたら再送を要求するなり何なりで対応します。  詳しく以下のサイトにのっているので参考にしてみてください。 http://www.jtw.zaq.ne.jp/kayakaya/new/kihon/text/error_control1.htm  ちょっと目的が違うページですが、軽く検索してみたところ、これが一番詳細に説明していましたので(^^;  参考になれば幸いです(..

coolguys
質問者

補足

考え方としては合っているんですね。 TRUEとFALSEの判断が上手くいっていないんで、 何かが違うのかなと思っていたのですが、流れの問題ですか。 流れ図的なものを書いても、コッチが送信で、こっちは受信 受信で送信?と書いているうちに今、どっちの処理をやっていたんだ? と分らなくなってしまって、ぐちゃぐちゃになってしまって分らなくなっていました。 もし良かったら、送信と受信の判断部の処理を簡単にでも良いので、 説明しては戴けませんでしょうか?

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.2

デバッガを使う,printf()デバッグを行う,などしてプログラム内部での処理の流れを確認してみては?

coolguys
質問者

補足

デバッカは当然使っていますし、 TRACE0なども用いています。 そのほかの使い方ということでしょうか? 詳細にお願いできませんか? どこの流れを確認したら良いのでしょうか?

  • honiyon
  • ベストアンサー率37% (331/872)
回答No.1

こんにちは、honiyonです。  本来ならば、どのような動作にしたいのでしょうか?  メッセンジャーのようなもの(1対1での会話)にしたいのであれば、受信したデータは返信せずに画面に表示して終わりにすれば良いかと思います。

coolguys
質問者

補足

早速、ありがとうございます。 >本来ならば、どのような動作にしたいのでしょうか? >メッセンジャーのようなもの(1対1での会話)にしたいのであれば、受信したデータは返信せずに画面に表示して終わりにすれば良 >いかと思います。 送信側から、送信ボタンを押すことによって、何度でも送信が行えるようにしたいのです。 通信チェックプログラムのようなものともいえますが、 送ったデータを受信側で持っているデータと比較して正しいか誤っているかを判断したいんです。 私が、やってみたのは、送信データにフラグのようなものを付加して(送信側はONとか) 送信側なら、受信側から送ってきた物に返事は出さない。 というようにしたのですが、一度送信したら2度目は送り返さなくなってしまいました。 受信スレッド:recvfrom -> sendto 送信ボタン・イベント:sendto です。 よろしくお願いします。

関連するQ&A