- ベストアンサー
Socket通信プログラム
VisualC++6.0(SDK)を用いてSocket通信(UDP)プログラムを 作成(チャットやメッセンジャーのようなもの)しています。 送信用と受信用 1つのプログラムにて、送受信両方の機能を兼ね備えたものを作成しようと思っています。 で、送信側から送ったデータを受信側で処理(文字列の追加など)をして 受信側から送信側に送り戻したいとおもいます。 送信ボタンにより送信します。受信したデータはエディットボックスに表示します。 が、受信したデータを送り返すとしているために、 いつまでも、受信->送信->受信・・・・を繰り返してしまいます。 これを、正しく動作するようにするには、どのようにしたら良いのでしょうか? ご存知の方宜しくお願い致します。 足りないものがあったら、補足させていただきます。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
>ラッピング?? >似たような処理で覆い隠す?ということでしょうか。 大体そのような感じです。今回は「送信バッファへの操作を構造体への操作に置換できる」ような送信処理関数を考えています。 下半分の解説ですが,まず,前回の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を使ったほうがよかったかな?
その他の回答 (8)
- onosuke
- ベストアンサー率67% (310/456)
UDPだとそうなりますね。 パケットタイプのフィールドを活用すると,いい感じにUDP pingを実装できそう。
お礼
遅くなりましたが、 なんとか、なりました。 大変ありがとう御座いました。 また、質問したときは、よろしくお願いします。
- onosuke
- ベストアンサー率67% (310/456)
UDP自体が接続(セッション)を確立しない通信ですから,無理です。 必要ならTCPを使いましょう。TCPなら,エラー処理でそういった状態を捕まえられます。
お礼
回答ありがとうございます。 質問がどんどん、逆行しているようで申し訳ないです。 ということは、 試しと言うかで、1回送って返信作業をやって、 戻ってきたら、繋がっているということがある意味 接続確立テストということですね。
- onosuke
- ベストアンサー率67% (310/456)
> 確か、「構造体の中を連続した領域で確保せよ!」という命令の仕方があった気がするのですが、 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, ...); ... }
お礼
回答ありがとうございます。 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)
こんにちは、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をフラグに使用」という方がシンプルで良いかも知れません(笑 あまり良い回答ではないですが、参考になれば幸いです(..
お礼
>構造体の中を連続した領域で確保せよ! やっぱりそういうのがあるんですね。 補足お待ちしております。 使いこなせるかどうかは別ですが・・・(^^j 構造体は連続した値ではないので、 手動にて、連続したデータを自分で作ってしまう。ナルホド。〆(。。) やっぱり、中身は手動でも、データ定義のときに 構造体だとかっこいいですよね。 大変参考になりました。 ありがとうございました。
- honiyon
- ベストアンサー率37% (331/872)
こんにちは、honiyonです。 送受信部についての具体例です。 送受信パケットは、Packet構造体に収められているとします。 「~ xxxx ~」は処理の省略です。 /*** 送信ボタンを押された時の処理 ***/ void OnSendButtonClick(){ ////// パケット作成処理 Packet.DataType = true; //初送信を示す。 ~その他メッセージ等をPacketに詰め込む処理~ ////// データ送信処理 ~パケットを相手に送信する~ } /*** 受信を受けた時の処理 ***/ void OnRecvData(){ ~受信処理を行う~ ////// 初送信データなら返信する if (Packet.DataType){ Packet.DataType = false; //not 初送信として循環防止! ~返信処理~ ////// 返信されてきたデータならチェック } else { ~チェック処理~ } } こんな感じになると思います。 参考になれば幸いです(..
お礼
honiyonさん、回答いただきありがとうございます。 バッチリ b(^^)d 動作できました。 でも、一つだけ分からないことがあったために、 もう一つだけ甘えさせてもらってもよいでしょうか? Packet構造体にデータとフラグを詰め込んだとしますよね。 struct Packet{ bool Type; char Data[1000]; } とした場合に、送信時のsendtoにてキャストが出来ません。 sendtoの第2引数です。 で、今回は無理やりに文字列の1番目に力技にてフラグを入れたのですが、 構造体の方がスマートなやり方だと思っても出来ませんでした。 大変申し訳ありませんが、よろしくお願いできませんでしょうか?
- honiyon
- ベストアンサー率37% (331/872)
こんにちは、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 ちょっと目的が違うページですが、軽く検索してみたところ、これが一番詳細に説明していましたので(^^; 参考になれば幸いです(..
補足
考え方としては合っているんですね。 TRUEとFALSEの判断が上手くいっていないんで、 何かが違うのかなと思っていたのですが、流れの問題ですか。 流れ図的なものを書いても、コッチが送信で、こっちは受信 受信で送信?と書いているうちに今、どっちの処理をやっていたんだ? と分らなくなってしまって、ぐちゃぐちゃになってしまって分らなくなっていました。 もし良かったら、送信と受信の判断部の処理を簡単にでも良いので、 説明しては戴けませんでしょうか?
- onosuke
- ベストアンサー率67% (310/456)
デバッガを使う,printf()デバッグを行う,などしてプログラム内部での処理の流れを確認してみては?
補足
デバッカは当然使っていますし、 TRACE0なども用いています。 そのほかの使い方ということでしょうか? 詳細にお願いできませんか? どこの流れを確認したら良いのでしょうか?
- honiyon
- ベストアンサー率37% (331/872)
こんにちは、honiyonです。 本来ならば、どのような動作にしたいのでしょうか? メッセンジャーのようなもの(1対1での会話)にしたいのであれば、受信したデータは返信せずに画面に表示して終わりにすれば良いかと思います。
補足
早速、ありがとうございます。 >本来ならば、どのような動作にしたいのでしょうか? >メッセンジャーのようなもの(1対1での会話)にしたいのであれば、受信したデータは返信せずに画面に表示して終わりにすれば良 >いかと思います。 送信側から、送信ボタンを押すことによって、何度でも送信が行えるようにしたいのです。 通信チェックプログラムのようなものともいえますが、 送ったデータを受信側で持っているデータと比較して正しいか誤っているかを判断したいんです。 私が、やってみたのは、送信データにフラグのようなものを付加して(送信側はONとか) 送信側なら、受信側から送ってきた物に返事は出さない。 というようにしたのですが、一度送信したら2度目は送り返さなくなってしまいました。 受信スレッド:recvfrom -> sendto 送信ボタン・イベント:sendto です。 よろしくお願いします。
お礼
大変よく分かりました。 ポインタを取得していたんですか。 ありがとうございました。 少々話は変わるのですが、 ターゲットを見つける(接続が確立されている確認)というのも、 今までのように、返信データを用いて戻ってきたら、接続が確立されている という処理しかないのでしょうか? 繋がっている、繋がっていないということが、わかる方法はないのでしょうか? ご存知でしたら、教えていただけませんでしょうか。