構造体の送受信方法 – 固定ページ 2 – プログラミング – Home

通知
すべてクリア

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

固定ページ 2 / 2

YuO
 YuO
(@YuO)
ゲスト
結合: 22年前
投稿: 320
 

> std::string Send(reinterpret_cast<char*>(&A), rBytes); // ×

int *を強引にchar *に変換したところで,
それを出力してもintの値が出力されるわけではない事はわかっているのでしょうか。


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

レスありがとうございます。

> int *を強引にchar *に変換したところで,
> それを出力してもintの値が出力されるわけではない事はわかっているのでしょうか。

確かに・・・送信時に型キャストでcharに直して、受信時にそのままの型で受け取って
いて
int型に戻すという作業は行っていませんでした。
そこまで気が付きませんでした。

この場合、受信後にint型をatoi的な事をすれば良いのでしょうか?
int a = atoi( A.a ); // これだとエラーになってしまうので・・・

char型になってしまった構造体を元に戻す?
をしてくれるクラスみたいのはあるのでしょうか?


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

> この場合、受信後にint型をatoi的な事をすれば良いのでしょうか?

int を数値(バイナリ)ではなく、数字(テキスト)に変換して送ったのであれば
それでいいでしょう。
但し、同構造体内の文字列(?)と続けて送ると区切りが分からないとか、
そういう問題がおきないようには考慮してください。

また、バイナリのままキャストして送っていれば受信時にも再キャストすることに
なると思いますが、(ここで初めて)バイトオーダなどを意識する必要が出ます。

> char型になってしまった構造体を元に戻す?
> をしてくれるクラスみたいのはあるのでしょうか?

そんなクラスはありません。

単純なキャストで済めばそれまでですし、
それ以上のシリアライズ/デシリアライズを行うには(どこぞの規格にでも従わない限り)
構造体を定義した人(あなた)の判断が必要です。

ちなみに、テキストにシリアライズして送らないのであれば、ただのバイト列に
std::string を使うのは不適切です。(バイナリ内に '\0' が含まれるときなど…)
せめて std::vector<char> なりにした方がいいです。

シリアライズするなら std::stringstream なり使って << や >> など実装するのも
いいと思います。


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

補足

> せめて std::vector<char> なりにした方がいいです。

薦めているわけではありません。そもそも、キャストなんて必要悪なものですし、
まして reinterpret_cast は C++ のキャストの中でも特にお勧めできません。
# コンパイラに無理やり型を押しつけるのでプログラマの責任が増えます。
send / write のようなきわめて低水準の関数に直接渡すときくらいしか
使うべきではないと思ってます。

# std::string についても、最初の質問にはなかったと思いますが、なぜ突然.......?
# テキストに変換して送りたかったのでしょうか.......。


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

> テキストに変換して送りたかったのでしょうか.......。

すみません。こちらの記述ミスでした。
送信時は、stringの文字列を
構造体のメンバにコピーした後にsendで送っています。

typedef struct sample_struct{
int a;
char f[256];
sample_struct()
{
f[0] = '\0';
a = 0;
}
}sample;

void CLIENT_SEND(string Send){

   sample A;

   lstrcpy( A.f , Send.c_str() );
   send(Sock,reinterpret_cast<char*>(&A),sizeof(sample),0);

}

> そもそも、キャストなんて必要悪なものですし、
> まして reinterpret_cast は C++ のキャストの中でも特にお勧めできません。

ですが、ソケット通信のsendではreinterpret_cast以外には方法が無く
キャストしないと送れないのではないでしょうか?

int(バイナリ)もchar(テキスト)にキャストしないと
『2番目の引数を'int'から'char*'に変換できません。』
とエラーがでてしまいますし。


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

> ですが、ソケット通信のsendではreinterpret_cast以外には方法が無く
> キャストしないと送れないのではないでしょうか?

言葉が足りませんでした。send は read/write なみの低水準関数という認識です。
そして string は高水準という認識です。

>  send(Sock,reinterpret_cast<char*>(&A),sizeof(sample),0);

送受信が同じコンパイラの同じオプションで作られて、同じ環境で動かす前提であれば、
受信も同様にキャストするだけだと思います。
recv(Sock, reinterpret_cast<char*>(&B), ...

環境が違う場合、int のサイズが違ったり、バイトオーダが違ったり、
構造体のアライメントが違ったり、文字コードが違ったり、......
などを送受信時に全て考慮する必要があります。
サイズなどはあらかじめ一定のルールを決める、オーダは htonl などの関数を使う、等

> int(バイナリ)もchar(テキスト)にキャストしないと

もしかして誤解があるような。
例えば int の 2 を char に「キャスト」しても 1byte のバイナリ値 2 になるだけで
文字の '2' にはなりませんし、int を char に static_cast しても、
int* を char* に reinterpret_cast しても、基本的にはビットパターンベースの
変換がキャストですから、いずれもバイナリのままです。

型としては同じ char ですが、テキストの文字とバイトサイズのバイナリデータ、
文字列とバイト列は扱い上別物ですし、send/recv や read/write が扱う
char は単にバイト列であって文字列を意味しません。

> int a = atoi( A.a ); // これだとエラーになってしまうので・・・

例えば atoi が要求するのは(バイト列ではない)文字列ですので、
仮に int の A.a を char にキャストしてもうまく動きません。
キャストしたものはキャストで戻すのが基本です。

# Windows 以外のOSでソケットのAPIが若干変わってもいいなら、WSASend でバッファを
# 分けるとかすると書き方次第で送信時の string コピーは要らなくなりそうな....
# string では試してませんが。


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

レスありがとうございます。

> 送受信が同じコンパイラの同じオプションで作られて
> 同じ環境で動かす前提であれば、
> 受信も同様にキャストするだけだと思います。

送受信は同じコンパイラの同じオプションで作っていますが
ゲームに通信部分を組み込みした場合、同じ環境では動かす事は無いと思います。
同じ環境と言うのは、自分と相手のPC環境が一緒と言う事ですよね。

> 同じ環境で動かす前提であれば、受信も同様にキャストするだけだと思います。
> recv(Sock, reinterpret_cast<char*>(&B), ...

現在は私のPCで動かしているので、受信も同じ事をしています。
これが、違う環境の場合Banさんが挙げてくださった

> オーダは htonl などの関数を使う

などを使用すれば良いのですね。

> もしかして誤解があるような。

すみません。
勘違いしていたみたいです。

> # Windows 以外のOSでソケットのAPIが若干変わってもいいなら

実行環境は全てWindowsになる予定ですが・・・
(こういう意味ではない・・・?)


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

> 同じ環境と言うのは、自分と相手のPC環境が一緒と言う事ですよね。

そして、送信プログラムと受信プログラムの作成コンパイラとそのオプションも一緒。

バイトオーダは通常 CPU で決まるので、ぶっちゃけサーバも含めて
Wintel(Windows/Intel) 専用なら「ネットワークバイトオーダにする」という
慣習(?)を無視して放っておいてもとりあえず動くはずです。

C/C++ の保証する互換性は「コンパイル時(ソース)」レベルで、
出力される実行ファイルについての保証はあまりしないので、
int のサイズや、構造体のサイズなどがコンパイラのオプションや種類で
変わってしまう可能性があります。これは、両方を同じ設定のコンパイラで
作ってあげれば、とりあえず動くはずです。

文字コードも、Windows なら S-JIS/UNICODE位しか選択肢がありませんし、
意識するのは NT 系と 98 系の問題くらい同じコンパイラで作ればそう問題も
ないと思います。
# NT系と98系でAPIの挙動が違うという、今回とは無関係ない問題はあるでしょうが...。

> これが、違う環境の場合Banさんが挙げてくださった
>> オーダは htonl などの関数を使う
>などを使用すれば良いのですね。

そうですね。将来的にサーバ側は UNIX で、とかの予定があると考えておく
必要があります。(予定がなくても考慮はしておいた方がいいと個人的には思ってますが.
..)

実行環境は全てWindowsになる予定ですが・・・
(こういう意味ではない・・・?)

WSASend という関数は WinSock にしかありませんので、
開発(コンパイル)環境がWindows限定になってしまいます。
# UNIX 系でも同種の機能はありますが、ソース事態の書き方が違います。
# send / recv などの基本的なソケットはほぼそのままで動くのですが......。

WSASend の利点は、送信するバイト列を全て連続したメモリに格納する必要がないこと。
このアドレスから何バイト、次はこのアドレスから何バイト、.....まとめて送って。
というお願いができる。まぁ最適化云々は動いてから考えればいいことなので、欄外に。


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

レスありがとうございます。

実行環境が
intelとAMDの場合はバイトオーダを気にして作成しないといけないと言う事ですね。

reshiaさんが仰った
『MPI』を使うと良いかもと書かれていますが
『htonl』とどちらが良いのでしょうか?


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

> intelとAMDの場合はバイトオーダを気にして作成しないといけないと言う事ですね。

Intel と AMD ではバイトオーダーが違うかということならば、そんなことはありませ
ん。
AMD の CPU は Intel 互換なので、同じバイトオーダーを使っています。

Intel とは違うバイトオーダーを使っている代表格は、モトローラの CPU です。主に
Mac で使われています。
モトローラの CPU 上で動く Windows はありませんので、対象 OS を Windows に限定で
きるのなら、

> Wintel(Windows/Intel) 専用なら「ネットワークバイトオーダにする」という
> 慣習(?)を無視して放っておいてもとりあえず動くはずです。

ただし、C 言語は Windows 以外のさまざまな環境や、もちろん Mac でも使える言語で
すし、ソケット通信というのも同様に、他環境との通信が可能です。
特にネットワーク通信では、そういうことを踏まえて、ネットワーク上に送り出すデー
タはネットワークバイトオーダー(ビッグエンディアン。Intel とは逆で、モトローラ
派のエンディアン方式)に統一しましょう、というお約束があるわけです。


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

レス有難うございます。

> 対象 OS を Windows に限定できるのなら

限定の予定ですが、サーバー側はいずれはLinux上かもしれませんので
ネットワークバイトオーダーをを気にしながら作成しようと思います。

> というお約束があるわけです。

そうだったのですか!?
それでしたら、尚更バイトオーダを意識して作成しないといけないですね。

これから、通信部分を作り変えてみたいと思います。


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

返信する

投稿者名

投稿者メールアドレス

タイトル *

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