構造体の送受信方法 – プログラミング – Home

通知
すべてクリア

[解決済] 構造体の送受信方法

固定ページ 1 / 2

クリスタル
 クリスタル
(@クリスタル)
ゲスト
結合: 20年前
投稿: 11
Topic starter  

sendでデータの送信をする時に
第2引数に構造体を入れて送信したいと思っています。

下記の様な指定の仕方ですと、エラーは起きないのですが・・・
char buf[256] = { 0 };
send(clsock,buf,256,0);

// 構造体を第2引数に指定してみると。。。
typedef struct{
int a;
int b;
}sample;

sample A;
send(clsock,&A,sizeof(sample),0);

構造体を入れてコンパイルをすると
'send':2番目の引数を'sample *'から'const char*'に変換できません。
とエラーが出てしまいます。

この場合どう対処したらよいのでしょうか?
ご教授お願い致します。

開発環境:
VC++.netを使用しています。


引用未解決
トピックタグ
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

送るだけなら &A を reinterpret_cast<char*>(&A) にする。

但し、通信相手がまったく同じ環境でもない限り、
バイナリデータをそのまま send しても正しく recv できる保証はありません。

構造体のメモリ配置はコンパイラにより異なる可能性がありますし、
CPUのバイトオーダ等が異なることもありえます。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

追記:
sample の例だと、そもそもint のサイズが同じとは限らないというのもあります。


返信引用
reshia
 reshia
(@reshia)
ゲスト
結合: 20年前
投稿: 117
 

バイトオーダーの件は、MPIを利用するといいかも


返信引用
クリスタル
 クリスタル
(@クリスタル)
ゲスト
結合: 20年前
投稿: 11
Topic starter  

Banさん、reshiaさん
レスありがとうございます。

Banさんに教わりました方法で早速試してみました。
typedef struct{
  char buf[256];
}sample;

sample A;
send(clsock,reinterpret_cast<char*>(&A),sizeof(sample),0); // 送信
recv((SOCKET)wParam,reinterpret_cast<char*>(&A),sizeof(sample),0);// 受信

上記の送受信方法で、受信後に構造体のメンバを表示すると
『フフフフフフフフフフフフフフフフフフフフ』と表示された後に、送信した文字が表示されます。

これは、バイトオーダというのが原因なのでしょうか?
また、MPIというのは使用した事がないのですが、難しいものなのでしょうか?


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

バイトオーダのせいではありません。
(char では通常、バイトオーダの問題は発生しません)

VC のデバッグ版モジュールをブレイクしながらし見ているのであれば、
「フフフ」というのは未初期化のデータを示します。
(まだ値が設定されていない状態の時、VCはデバッグしやすいようにフフフを入れます)

よって、受信用の領域を初期化していない状態で recv を行った場合、
受信前の時点では「フフフ」になっていて、受信がすんだらその文字列が入る、
という挙動であれば特におかしくないと思います。こういう挙動ですか?

# ちなみに、フフフが入るのは、VC のデバッグ版のときだけです。
# C/C++ としては、その値は「不定(何になるか分からない)」です。
# 毎回値が違うと確認しにくかったりするのでデバッグ用に同じ値にしてくれるのです。


返信引用
クリスタル
 クリスタル
(@クリスタル)
ゲスト
結合: 20年前
投稿: 11
Topic starter  

まだ試していませんが受信前に表示しても、フフフと出ると思います(後ほど試してみま
す)。
受信した文字はダイアログボックスに貼り付けたエディット ボックスに表示していま
す。

受信前に構造体を初期化してみたのですが
メッセージ受信時にエラーが出てしまい、強制終了されてしまいます。
memcpy( &A , 0 , sizeof(sample)); // 初期化処理

構造体を初期化する場合、他の手段があるのでしょうか?


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> まだ試していませんが受信前に表示しても、フフフと出ると思います
(後ほど試してみます)。

ん?
コードを見てないのでなんともいえませんが、想定通りなら私も多分出ると思いますよ。
> 受信前の時点では「フフフ」になっていて、受信がすんだらその文字列が入る
先に書いたとおり。
# で、もしもこのフフフが、例えばエディットコントロール等に出てたとしたら
# それは「未初期化変数を参照してる」バグを持ったコードです。
# 警告レベルによっては検出できるかも。

> 上記の送受信方法で、受信後に構造体のメンバを表示すると
> 『フフフフフフフフフフフフフフフフフフフフ』と表示された後に、
> 送信した文字が表示されます。

この観測方法を明確にしてもらわないと、あくまで推測の域を出ませんが。

フフフと表示されるのはどのタイミングなのか。
「後に」とは何をした後なのか。何がきっかけで文字列が表示されるのか。
# 非同期実行してて受信したタイミングで切り替わるとか、
# ステップ実行でF10おしたら切り替わるとか、........

そもそも、上記のsend と recv をどのタイミングで書いているのか。
どうやってその結果を確認しているのか。何かに出力したりとかしてます?
よく見ると recv の引数がウィンドウプロシージャの引数っぽいですが、
send と recv の呼び出し関係はどうなってます?

私には両者が同一プロセス内なのか、違うのかすら分からないわけですが、
こういう部分に関する情報をもっと示していただけると、
もう少し踏み込んだ回答や説明ができるかもしれません。

> 構造体を初期化する場合、他の手段があるのでしょうか?

例えばコンストラクタを書くとか、まぁいろいろあると思いますが、
とりあえずその memcpy を memset に直せば多分通ると思います。
そのままだと 0メモリ参照で落ちるはず。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> この観測方法を明確にしてもらわないと、あくまで推測の域を出ませんが。

おぉっと、失礼。

> 受信した文字はダイアログボックスに貼り付けたエディット ボックスに
> 表示しています。

見落としてました........orz

というわけで、フフフが表示されてしまうコードはバグ持ちです。

リリース版だとどんな値が入るか分からないので、最悪アプリが突然落ちたりする
危険があります。

対策は適切な初期化。memset に直せばとりあえず動く筈です。
ただ、その char の配列が文字列として使われるなら必ずしも全体を 0 初期化する
必要はないので、こんなのでも可。

typedef struct sample_struct
{
  char buf[256];

  sample_struct(){ buf[0] = '\0'; }
}sample;

受信文字列長が変わるときは末尾に文字列終端'\0'がつくように注意して下さい。


返信引用
REE
 REE
(@REE)
ゲスト
結合: 23年前
投稿: 240
 

今回のケースは単に受信できていないだけと思われます。


返信引用
クリスタル
 クリスタル
(@クリスタル)
ゲスト
結合: 20年前
投稿: 11
Topic starter  

REEさんレスありがとうございます。
Banさんお返事が遅くなり申し訳ございません。

> まだ試していませんが受信前に表示しても、フフフと出ると思います

コードを書き換えるのに時間が掛かるため、先に初期化のほうを試して見ました。。

> よく見ると recv の引数がウィンドウプロシージャの引数っぽいですが、
> send と recv の呼び出し関係はどうなってます?

簡単な通信ゲームを作成しようと思っており、現在はチャットの部分を作成していま
す。
受信時は、プロシージャ内で『case FD_READ:』でキューに値が入っていれば、recvを呼
ぶようにしています。
sendは現在は、『Enter』を押されたときに呼ぶようにしています。
今後は毎回呼ばれるようにコードを書き換える予定です。
送受信は違うプロセスで稼働しています。

Banさんの仰った通り初期化を行いました所、何事も無く文字が表示されました。
ただ、構造体の中にint型のメンバを付け足すと文字の受信が行えなくなりました。

typedef struct sample_struct
{
int a;
  char buf[256];

  sample_struct(){ buf[0] = '\0'; }
}sample;

これがバイトオーダと言うものなのでしょうか?


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> これがバイトオーダと言うものなのでしょうか?

いいえ、まったく別の問題と思われます。

「バイトオーダ」については調べてみましたか。参考までに綴りは「byte order」です。
例えば Intel の CPU ならリトルエンディアンと呼ばれるオーダ(順番)です。

> ただ、構造体の中にint型のメンバを付け足すと文字の受信が行えなくなりました。

バイトオーダの問題で普通こんなことは起きません。
前述のように、文字列を格納している char の配列には直接影響しないのです。

おそらくそれ以前の問題でどこか間違っているものと思いますが、
どういう挙動になったのか、どんなコードを書いたのかさえ分かりませんし、
提示の情報のみではそれ以上はなんともいえません。
C言語の型や構造体のメモリ配置について理解は十分ですか?

# デバッガを使いこなせばコードを修正しなくてもフフフは表示できたような。


返信引用
クリスタル
 クリスタル
(@クリスタル)
ゲスト
結合: 20年前
投稿: 11
Topic starter  

> C言語の型や構造体のメモリ配置について理解は十分ですか?

例えばこの用な構造体があった場合

struct sample {
int a;
char b[10];
int c;
};

上からメモリ上で、
2バイト
10バイト
2バイト
と割り振られるのですよね。

> どういう挙動になったのか、どんなコードを書いたのかさえ分かりませんし、
> 帝Dフ情報のみではそれ以上はなんともいえません。

申し訳ございません。
どの程度までコードをここに載せれば良いのか迷っていたので。

構造体にメンバを増やしても何とか送受信、文字表ヲが出来るようになりました。
表ヲされなかった原因は、stringクラスに構造体を丸ごと代入していたのが悪かったみ
たいです。

> サーバー、クライアント間で通信している構造体です。
typedef struct sample_struct{
int a;
char f[256];
sample_struct()
{
f[0] = '\0';
a = 0;
}
}sample;

sample A;

> std::string Send(reinterpret_cast<char*>(&A), rBytes); // ×
> Sentence_Dialog(Send.c_str()); // 送信フェーズ

上記のですと表ヲuDッ敗しますが
下記の方法ですと無事に表ヲされます。

> std::string Send( A.f, sizeof(A.f) ); // ○
> Sentence_Dialog(Send.c_str()); // 送信フェーズ

受信したint型をchar型と同じように表ヲさせると
『』となってしまうのが難点ですが。


返信引用
とおりすがり
 とおりすがり
(@とおりすがり)
ゲスト
結合: 23年前
投稿: 180
 

> struct sample {
> int a;
> char b[10];
> int c;
> };
>
> 上からメモリ上で、
> 2バイト
> 10バイト
> 2バイト
> と割り振られるのですよね。

そうとは限りません。
コンパイラ等の環境にも依存しますし、フィールドとフィールドの間にパディングという
埋め草領域ができる場合もあります。(まあ、今回はなさそうですが)
intが2バイトという保証もありません。sizeof(int)で確認して下さい。


返信引用
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

> コンパイラ等の環境にも依存しますし、フィールドとフィールドの間にパディングと
いう
> 埋め草領域ができる場合もあります。(まあ、今回はなさそうですが)

ありませんか?
コンパイルオプションにもよりますが、sizeof( int ) = 4 でアラインメント 4 と仮定
した場合、
上から順に 4 バイト、10 バイト、パディング 2 バイト、4 バイト のような。
VC++ 7.1 のデバッグモード(コンパイルオプションはデフォルトのまま)でやったら、
そうなりました。


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました