Re: 通信データ型 ( No.1 ) |
- 名前:かたぱると 日時:2007/10/29 13:46
こんなのはどうでしょうか。
enum SEND_DATA_TYPE//タイプ
{
TYPE_A,
TYPE_B
};
struct Network_Data // 実際に送信するデータ
{
SEND_DATA_TYPE type; //データタイプ
char work[256]; // ※@
};
struct Type_A_Data // データパターンA
{
int data_1;
int data_2;
int data_3;
};
struct Type_B_Data // データパターンB
{
double data_4;
char data_5[80];
int data_6;
};
<送信側プログラム>
SEND_DATA_TYPE mode;
Network_Data base;
switch( mode ) // ←モードで詰め込むデータ型を分ける
{
case TYPE_A:
// Aデータ型として詰め込む場合
Type_A_Data *a_data;
base.type = TYPE_A;
a_data = (Type_A_Data *)&base.work;
a_data->data_1 = 10;
a_data->data_2 = 20;
a_data->data_3 = GetColor( 255 , 255 , 255 );
break;
case TYPE_B:
// Bデータ型として詰め込む場合
Type_B_Data *b_data;
base.type = TYPE_B;
b_data = (Type_B_Data *)&base.work;
b_data->data_4 = 100;
strcpy( b_data->data_5 , "文字列ッス" );
b_data->data_6 = GetColor( 100 , 100 , 255 );
break;
}
// baseを送信する
<受信側プログラム>
Network_Data base; // ←受信したデータ
switch( base.type ) // ←送られてきたタイプで分ける
{
case TYPE_A:
Type_A_Data *a_data;
a_data = (Type_A_Data *)&base.work;
// 値を使う
DrawPixel( a_data->data_1 , a_data->data_2 , a_data->data_3 );
break;
case TYPE_B:
Type_B_Data *b_data;
b_data = (Type_B_Data *)&base.work;
// 値を使う
if( b_data->data_4 >=30 )
{
DrawString( 10 , 10 , b_data->data_5 , b_data->data_6 );
}
break;
}
詰め込むデータサイズは
※@より小さくなくてはいけないのでそこは管理&調整
|
Re: 通信データ型 ( No.2 ) |
- 名前:ライブラリ使用者 日時:2007/10/29 23:47
ありがとうございます!
うまく受信できました。
が、自分がいまだ理屈をできていない(−−;
struct Type_C_Data // データパターンC
{
int Color;
char* pchar;
};
これを試すと、始めに私が嵌った罠に陥るみたいですね。。
char* pchar というchar型のポインタを使用する場合と、かたぱるとさんの提起されているchar work[256]でどう違ってくるのですか?
b_data = (Type_B_Data *)&base.work;
&base.workここのキャストで結局は同じcharのポインタを指しますよね。
また、可変のデータは扱えないということなのでしょうか?
※@の最大サイズに注意とともに、データ量に関わらず256+4バイトのデータを送り続けなかればならない?
|
Re: 通信データ型 ( No.3 ) |
- 名前:ライブラリ使用者 日時:2007/10/29 23:49
参考までにサンプルソース記載します。
--送信側-----------------------------------------
#include <windows.h>
#include <process.h>
#include "DxLib.h"
enum SEND_DATA_TYPE//タイプ
{
TYPE_A,
TYPE_B,
TYPE_C
};
struct Network_Data // 実際に送信するデータ
{
SEND_DATA_TYPE type; //データタイプ
char work[256]; // ※@
};
struct Type_A_Data // データパターンA
{
int data_1;
int data_2;
int data_3;
};
struct Type_B_Data // データパターンB
{
double data_4;
char data_5[80];
int data_6;
};
struct Type_C_Data // データパターンC
{
int Color;
char* pchar;
};
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
int Handle;
int DataLength;
IPDATA ip;
ip.d1 = 192;
ip.d2 = 168;
ip.d3 = 1;
ip.d4 = 2;
//タイトルの決定
SetMainWindowText("SendSample");
//画像モードの決定
//SetGraphMode(XPIXEL,YPIXEL,GAMEBIT);
//背景色を設定
SetBackgroundColor(255,255,255);
//常に動作させる
SetAlwaysRunFlag(TRUE);
//ウインドウモードでのプログラム起動
//デフォルトではフルスクリーンモード起動となる
ChangeWindowMode(TRUE);
//DxLib初期化
if(DxLib_Init() == -1)
return FALSE;
//描画先を裏画面にする
//ScreenFlip()を使い描画する(描画後は裏画面クリアClearDrawScreen()する)
SetDrawScreen(DX_SCREEN_BACK);
////////////////////////////////////////////////////////////////////////////
//ここまで初期化
//通信接続する
if((Handle = ConnectNetWork(ip)) == -1)
{
//DxLib終了
DxLib_End();
return FALSE;
}
///////////////////////////////////////////////////////////////////////////
//データ作成する
SEND_DATA_TYPE mode;
Network_Data base;
mode = TYPE_C;
switch( mode ) // ←モードで詰め込むデータ型を分ける
{
case TYPE_A:
// Aデータ型として詰め込む場合
Type_A_Data *a_data;
base.type = TYPE_A;
a_data = (Type_A_Data *)&base.work;
a_data->data_1 = 10;
a_data->data_2 = 20;
a_data->data_3 = GetColor( 255 , 255 , 255 );
DataLength = sizeof(Type_A_Data) + 256;//256 = work[256]
break;
case TYPE_B:
// Bデータ型として詰め込む場合
Type_B_Data *b_data;
base.type = TYPE_B;
b_data = (Type_B_Data *)&base.work;
b_data->data_4 = 100;
strcpy( b_data->data_5 , "文字列ッス" );
b_data->data_6 = GetColor( 100 , 100 , 255 );
DataLength = sizeof(Type_B_Data) + 256;//256 = work[256]
break;
case TYPE_C:
// Cデータ型として詰め込む場合
Type_C_Data *c_data;
base.type = TYPE_C;
c_data = (Type_C_Data *)&base.work;
c_data->Color = 7777;
c_data->pchar = "abcde";
DataLength = strlen(c_data->pchar)+1 + 256;//256 = work[256]
break;
}
////////////////////////////////////////////////////////////////////////////
//データ送信する
NetWorkSend(Handle,(void*)&base,DataLength);
//裏画面クリア
ClearDrawScreen();
//裏画面を表示させる
ScreenFlip();
WaitKey();
//通信接続を切断する
CloseNetWork(Handle);
//DxLib終了
DxLib_End();
return 0;
}
---受信---------------------------------------
#include <windows.h>
#include <process.h>
#include "DxLib.h"
enum SEND_DATA_TYPE//タイプ
{
TYPE_A,
TYPE_B,
TYPE_C
};
struct Network_Data // 実際に送信するデータ
{
SEND_DATA_TYPE type; //データタイプ
char work[256]; // ※@
};
struct Type_A_Data // データパターンA
{
int data_1;
int data_2;
int data_3;
};
struct Type_B_Data // データパターンB
{
double data_4;
char data_5[80];
int data_6;
};
struct Type_C_Data // データパターンC
{
int Color;
char* pchar;
};
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
int Handle;
int DataLength;
char buff[1024];
int color;
char hh[256];
//タイトルの決定
SetMainWindowText("RecvSample");
//画像モードの決定
//SetGraphMode(XPIXEL,YPIXEL,GAMEBIT);
//背景色を設定
SetBackgroundColor(255,255,255);
//常に動作させる
SetAlwaysRunFlag(TRUE);
//ウインドウモードでのプログラム起動
//デフォルトではフルスクリーンモード起動となる
ChangeWindowMode(TRUE);
//DxLib初期化
if(DxLib_Init() == -1)
return FALSE;
//描画先を裏画面にする
//ScreenFlip()を使い描画する(描画後は裏画面クリアClearDrawScreen()する)
SetDrawScreen(DX_SCREEN_BACK);
////////////////////////////////////////////////////////////////////////////
//ここまで初期化
//通信接続待ちにする
if(PreparationListenNetWork() == -1)
{
//DxLib終了
DxLib_End();
return FALSE;
}
Handle = -1;
while(Handle == -1)
{
if(ProcessMessage() == -1)
{
break;
}
//新しい接続先を取得する
Handle = GetNewAcceptNetWork();
}
while(1)
{
if(ProcessMessage() == -1)
{
//×でのみ終了
break;
}
DataLength = GetNetWorkDataLength(Handle);
if(DataLength > 0)
{
NetWorkRecv(Handle,buff,DataLength);
//ここでbuffの中身を確認する!
Network_Data* base;
base = (Network_Data*)&buff;
switch( base->type ) // ←送られてきたタイプで分ける
{
case TYPE_A:
Type_A_Data *a_data;
a_data = (Type_A_Data *)&base->work;
// 値を使う
DrawPixel( a_data->data_1 , a_data->data_2 , a_data->data_3 );
break;
case TYPE_B:
Type_B_Data *b_data;
b_data = (Type_B_Data *)&base->work;
// 値を使う
if( b_data->data_4 >=30 )
{
DrawString( 10 , 10 , b_data->data_5 , b_data->data_6 );
}
break;
case TYPE_C:
Type_C_Data *c_data;
c_data = (Type_C_Data *)&base->work;
color = c_data->Color;
strcpy(hh,c_data->pchar);
break;
}
break;
}
//裏画面クリア
ClearDrawScreen();
//裏画面を表示させる
ScreenFlip();
WaitTimer(100);
}
//通信接続を切断する
CloseNetWork(Handle);
//通信接続待ちを解除する
StopListenNetWork();
//DxLib終了
DxLib_End();
return 0;
}
|
Re: 通信データ型 ( No.4 ) |
- 名前:かたぱると 日時:2007/10/30 01:53
【その壱:ポインタについて】
えー、まずポインタの説明ですが
ttp://homepage2.nifty.com/natupaji/DxLib/lecture/lectureOthers1.html
に管理者様のポインタ説明がございます。
かいつまんで説明しますと、
ポインタとは「実機のメモリ内アドレス」
なので、この情報を送信しても
受信側マシンの同じアドレスに何が入っているかは解らない訳です。
なので、何があるか解らないアドレスを送るのではなく、
データの実態を送ってあげる必要があります。
つまり、
char* pchar というchar型のポインタ ←アドレス(受信側は何が入っているか解らない)
char work[256] ← 実態のデータそのもの
というわけです。
実態があるものに対してそこのメモリアドレスを参照してデータを詰め込む、
といったポインタの使い方が
b_data = (Type_B_Data *)&base.work;
になります。
実際に送信しているデータは
Network_Data構造体のchar work[256] の為、
送信データは実態になります。
※POINT:他人にアドレスだけ教えてもなんのこっちゃ? 実態を渡そう。ポインタは自分のもの!
【その弐:送受信用データサイズについて】
送信に使っている
DataLengthですが、実際に送受信する構造体はNetwork_Dataなので、
DataLength = sizeof(Network_Data);
でOKのはずです。
【その参:workのサイズについて】
char work[256];
の256という数字は私が勝手に今回書きました。
実際に運用する場合のworkのサイズは
使用予定のデータパターン(Type_A_Data等)
の『最大サイズ』です。
<例>
Type_A_Data :サイズ100
Type_B_Data :サイズ40
Type_C_Data :サイズ120
だった場合、workのサイズが80とかですと
Type_AやType_Cのデータはあふれてしまいメモリ破壊が起きます。
この場合最低でもType_Cのデータが格納できる120のサイズは確保しておかないといけません。
workを可変で持たせる手段もない事は無いですが、
決めうちでサイズを決めておくと
送受信のサイズが一定になるので管理が楽です。
と、駆け足で説明しましたがご理解頂けましたでしょうか。
解りづらい説明でホンマスイマセン。
|
Re: 通信データ型 ( No.5 ) |
- 名前:ライブラリ使用者 日時:2007/10/30 02:03
もう1つ方法思い浮かびました。(ちと強引ですが)
(送信)
char* pdata = new char[可変サイズ]
可変サイズはsizeof(int)+α(データ詳細)
int Type = TypeA;
memcpy(pdata,&Type,sizeof(int);
memcpy(&pdata[sizeof(int)],データ中身,α);
これでpdataには最初の4バイトにタイプを示すint値が、残りのバイトがデータ中身を示す連続メモリになると思います。
(受信)
switch(*(int*)pdata)
{
case TypeA:
(データ)&pdata[sizeoof(int)];
}
これならば可変データも扱えると思うのですが、問題あるでしょうか?
(データサイズなどの管理は重要でしょうが)
|
Re: 通信データ型 ( No.6 ) |
- 名前:通 日時:2007/10/30 06:38
通信を行う場合の構造体では、
通常No5で紹介されているような構造をとります。
構造体の開始位置からのオフセットでデータを
詰めていくことでパケットを作成します。
送信する場合にポインタで持っているデータの
サイズ長を指定する必要があるため、通常は
構造体にデータのサイズも含めます。
|
Re: 通信データ型 ( No.7 ) |
- 名前:かたぱると 日時:2007/10/30 09:52
成程納得。
通信をマトモにやっていなかった為
まだまだ頭が固かったようです。
DiretShow等のストリームデータに似てますね。
|
Re: 通信データ型 ( No.8 ) |
- 名前:Will 日時:2007/10/30 10:10
私が可変サイズの通信データの構造体を使う時は以下のようにしています。
typedef struct SendMessageType
{
int Type; //送受信データタイプ
unsigned long lenght; //データレングス(私はこれがあるほうが好き)
unsigned char data[1]; //データ詳細(コンパイラによってはサイズ0で定義できるものもある)
} ST_SENDMESSAGETYPE;
// 送信側
ST_SENDMESSAGETYPE *psend;
データサイズ = (sizeof(ST_SENDMESSAGETYPE) - 1) + 可変データサイズ;
// -1はdata[1]のサイズ分
psend = new char[サイズ];
// データのセット
psend->Type = データタイプ;
// data部をそれぞれのデータ型に合った構造体に変換してデータをセットする
ST_TYPE_A_DATA *pdata;
pdata = (ST_TYPE_A_DATA*)(psend->data);
pdata->data_1 = 0;
・
・
・
// 受信側
ST_SENDMESSAGETYPE *pdata;
pdata = (ST_SENDMESSAGETYPE*)受信データの先頭アドレス;
switch (pdata->Type) {
case タイプA:
// dataをそれぞれのタイプにあった構造体に変換しデータを参照する
・
・
ってな感じでしょうか。
これのほうが、構造体を使用できるのでソースの見易さ、データの管理がしやすいと思います。
|
Re: 通信データ型 ( No.9 ) |
- 名前:ライブラリ使用者 日時:2007/10/30 21:56
>かたぱるとさん
レス遅れましたが、説明有難う御座います。
そうじゃないかと思っていた通りでした。
夜は、ずっとこのHPを開いたままソースとにらめっこ状態であったためレスに気付ずでした。
>通さん
コメントありがとう御座います。
>Willさん
参考になります。盲点的な使い方でした(やや反則気味な気もしますがw
構造体として使えるのは、やり易そうだと思います。
ポインタは使い方間違えると痛い反面、色々手段がありますね。勉強になります。
これで今度こそ(?)、仕上がりそうです。
ご助言本当に有難う御座います。
|
Re: 通信データ型 ( No.10 ) |
- 名前:ライブラリ使用者 日時:2007/11/03 01:25
くじけそうです。。
また質問です。
送信データは蓄積され、一気に取得してしまうのでしょうか?
NetWorkSend()
NetWorkSend()
GetNetWorkDataLength()
NetWorkRecv()
このような順番で通信相手との通信が成り立ったとします。この時、未受信のデータ量は単順に×2倍に膨れていました。
これは仕様なのでしょうか?
受信したことを確認した後、改めて次ぎの送信をするようプログラムするしかないのでしょうか?
|
Re: 通信データ型 ( No.11 ) |
- 名前:管理人 日時:2007/11/03 21:37
仕様となります。
ただ、NetWorkRecv では、必ずしも GetNetWorkDataLength で取得できるデータサイズ分
一度に取得する必要はありませんので、次のような書き方をすることが出来ます。
送信側
int type_length[2]; // 0:type 1:length
SENDTYPE0DATA data0; // ←これが送りたいデータだとします
〜〜〜〜
data0 に情報をセット
〜〜〜〜
type_length[0] = 0; //タイプをセット
type_length[1] = sizeof( SENDTYPE0DATA ); // データ長をセット
// タイプとデータ長を送信
NetWorkSend( nethand, type_length, sizeof( int ) * 2 );
// データを送信
NetWorkSend( nethand, &data0, sizeof( SENDTYPE0DATA ) );
受信側
int type_length[2]; // 0:type 1:length
void *buffer;
SENDTYPE0DATA *data0;
// タイプとデータ長分の情報を受信しているか調べる
if( GetNetWorkSendDataLength( nethandle ) > sizeof( int ) * 2 )
{
// タイプとデータ長を読み込む(データは破棄しない)
NetWorkRecvToPeek( nethandle, &type_length, sizeof( int ) * 2 );
// 残りの受信データがデータ長と同じかそれ以上あるか調べる
if( type_length[1] + sizeof( int ) * 2 <= GetNetWorkSendDataLength( nethandle ) )
{
// 改めてタイプとデータ長の情報を通信ハンドル上のバッファから削除する
NetWorkRecv( nethandle, type_length, sizeof( int ) * 2 );
// データを格納するバッファを確保
buffer = new char[type_length[1]];
// 読み込み
NewWorkRecv( nethandle, buffer, type_length[1] );
switch( type )
{
case 0:
data0 = (SENDTYPE0DATA *)buffer;
〜〜〜〜〜〜
受信した情報を処理
〜〜〜〜〜〜
break;
}
delete []buffer;
}
}
|
Re: 通信データ型 ( No.12 ) |
- 名前:ライブラリ使用者 日時:2007/11/04 00:13
回答有難う御座います。
そうですか、仕様ですか。。
元もとの質問で書いた、
NetWorkSend()
NetWorkSend()
GetNetWorkDataLength()
NetWorkRecv()
となるタイミングがあるのが問題でして。。
仕様であれば、ソース改善しないといけない訳ですね。。
ループで必要なバイト数分づつ処理させていくことにします。
一応確認の意味を込めて。
NetWorkSend()1回分毎に読み分けられないでしょうか?
過去ログの2006/12/30 19:18
「通信関係の使用ポートについて」にて、
DXライブラリの通信機能は NetWorkSend 関数で渡されたデータに
ちょっとした追加情報を加えて飛ばしていますので(NetWorkSend に渡された
データの長さを先頭4バイトに加えて相手にパケットを飛ばしている)
とありますので、そのパケットを利用してそこまでをNetWorkRecv()で受信するということはムリでしょうか?
|
Re: 通信データ型 ( No.13 ) |
- 名前:管理人 日時:2007/11/08 13:40
> そのパケットを利用してそこまでをNetWorkRecv()で受信するということはムリでしょうか?
現状のソースでは受信が完了した時点でサイズ情報の4バイトは破棄してしまって
いるので現時点では不可能ですが、通信部分のプログラムを書き換えれば実現できます。
実装する場合は受信データがどれだけ溜まっても GetNetWorkDataLength で
取得出来るデータ量は時間的に一番古い受信データのサイズのみとなると思いますが、
それで宜しいでしょうか?(因みに今までの機能との互換性を保つために「新仕様の
通信処理を行うかどうか」を設定する関数も追加することになると思います)
|
Re: 通信データ型 ( No.14 ) |
- 名前:ライブラリ使用者 日時:2007/11/09 00:27
要望として上げておきながらではありますが、機能追加は無しでいいです。
理由:互換性のため、設定追加が必要とのことであり汎用性が薄れるということ。
なにより、今回の件は私のソースの記述力不足によるものと判断できる点が多い為です。
ご検討頂き有難う御座いました。
|
|