ソケット通信の送受信 – 固定ページ 2 – プログラミング – Home

通知
すべてクリア

[解決済] ソケット通信の送受信

固定ページ 2 / 2

に
 に
(@に)
ゲスト
結合: 18年前
投稿: 13
 

> http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/lame-list.html
読み物としては面白かったんですが、初めてでこれを読まされたら
ちときついかと。

>こまったちゃん
geekなぺーじ
http://www.geekpage.jp/

ノンブロッキングソケット
http://www.geekpage.jp/programming/winsock/nonblocking.php
とかもオススメです。(回答者の人たちは、このへんの知識がある
前程で話しをしていると思います)

>多分、TCP/IPの通信にもあると思います。(調べてません)
RFCの話ですね。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

> これとはまた問題のレイヤが違うと思います。
> IP層にはそのIPデータグラムの長さを示すフィールドがありますが、
> それはそれとして、TCP層に載る通信データは、IP層での分割がどうなっているか気
> にせず(気にしてはならず)、継ぎ目のないデータがだだ流れになっている
> という仕様ですから。

> だから、TCPを使うプログラムが自前でデータの区切り方を定義する必要が
> あるわけで。
なるほど、了解です。

ということは、
 RS232Cの場合、STX 02Hの後に何番目のデータかとかの区切り番号をつければ
いいのですね。

なぜ、RS232Cにこだわるかと言うと。、極一般的なフォーマットで
解りやすいかと思いました。

>オススメはSMTP (RFC 2821) やPOP3 (RFC 1939) あたりかな。
了解です。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

通信のフォーマットを決める時のポイントは、
受け取った時になるべく一定の手順で処理できるようにすると
言う事かな。(うまくデザインすると処理が楽になる)

デリミタを決めれば、とりあえず、一つのデータの区切りは認識可能になるから
一回の受信した中にデリミタが無ければ、次の受信までする必要がある事を
判断できるし、受信した中にデリミタがあってその後に更にデータが繋がって
いれば、それは次の受信結果とくっつけて処理する必要があると判断できる。
一回の受信で何個一気に受信されようが、デリミタを頼りにデータを分割し、
きちんとデータが完結していなければ、次の受信を待てばいいと言う事。
但し、デリミタに使うものはそのままでは受信データの一部としては使えないから
それは注意する必要がある。
もっと突っ込んでフォーマットを決めれば、色々な事を予め行う事ができるように
なるからやり取りするデータの形がきちんと決まっているならフォーマットを
うまく決める事で通信がやりやすくなると思う。
但し、色々な制御に必要な情報を付け加えると電文が太るので転送効率は
若干落ちるかもそれが問題になるかどうかはシステム次第だろうけれど。


返信引用
こまったちゃん
 こまったちゃん
(@こまったちゃん)
ゲスト
結合: 18年前
投稿: 39
Topic starter  

みなさんありがとうございます

>#う--ん、通信のソフトを作ったことないのかな?
通信のプログラムを書くのが初めてです。
その為にフォーマットのイメージがわかっていないです。

例えば下記のデータを送信する時
char Company[6]={あいうえお};
char SName[6]={かきくけこ};
char FName[6]={さしすせそ};
char buff[256];

sprintf(buff, %s\n%s\n%s\n, Company, SName, FName);

このbuffがフォーマットで\nがデリミタになりますか?


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 22年前
投稿: 1301
 

> このbuffがフォーマットで\nがデリミタになりますか?

やってみそ。
そいつが複数連続orブツ切りに送られてきても受信側できちんと復元できればそれでよ
し。
復元できなかったらフォーマットに不備がある。


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

フォーマットは %s\n%s\n%s\n
デリミタは \n
作成済み電文 (フォーマット済み通信本文) が buff
ということだ。

提示の例はいっぺんに送る場合だな。必ずしもそうでなくていい。
分割して送ってもまったく問題ない。
sprintf(buf, %s\n, Company); send(..buf..);
sprintf(buf, %s\n, SName); send(..buf..);
sprintf(buf, %s\n, FName); send(..buf..);

受信側の擬似コードを書いておこう。

受信済み電文=0バイト;
for (recv が正値を返している限り) {
 recv() の結果を受信済み電文の末尾に結合;
 while (受信済み電文に \n があれば) {
  受信済み電文の先頭から \n までを取り出す;
  受信済み電文の先頭から \n までを捨てる; // \n も捨てる
 }
}
// ここに到着したら recv が0か負を返した、つまり送信側から close された

これは要するに fgets と同じ動作なわけだ。

んで recv をブロッキングモードで使う場合、実際にデータを受信するまでは
処理が帰ってこない=このルーチンは停止してしまうわけで、
イベントハンドラ内部にこんなコードを書いちゃうとまずいのだな。
送受信をUIとは別のスレッドでやるか
WSAAsyncSelect 等を使うか
あたりの対処が必要になるわけだな。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

> このbuffがフォーマットで\nがデリミタになりますか?
Company, SName, FName のMAXSIZEを決めといたほうがいいと思います。
あと、空白部分を「\0」にするのかスペーズ「\x20」するのか決めたほうが
いいと思います。


返信引用
yoh2
 yoh2
(@yoh2)
ゲスト
結合: 18年前
投稿: 70
 

ちなみに、テキストベースのやりとりが中心になるプロトコルの場合、デリミタがLF('\n')ではなく、
CR+LF (\r\n) になることが多いです。
SMTPとPOP3は、メール本文の部分を除くとCR+LFが各コマンドやレスポンスの区切りになっていますし、
HTTPやSIPもヘッダフィールドの区切りやヘッダとボディの区切にCR+LFが使われています。

なお、別に'\n'が悪いと言っているわけでなく、単に傾向を紹介してみただけです。
'\n'一文字にすると、Cでのデリミタ判定部分が若干楽になりますしね。

> Company, SName, FName のMAXSIZEを決めといたほうがいいと思います。

同意。上限を決めておくと、'\n'が出てくるまでのバイト数を見積もれるので、受信バッファを
固定長で取っておくことができますからね。
まあ、受信バッファはメモリが許す限りで可変長に取る、としてもいいのですが、無駄に面倒になるだけか
も。

> あと、空白部分を「\0」にするのかスペーズ「\x20」するのか決めたほうが
> いいと思います。

これ、元コードのサーバ側m_chCompany、m_chSName、m_chFNameの、受信データが入らない部分のこ

でしょうか。
見方によっては、受信文字列の最後にヌル文字を入れること(または受信に先立ったバッファのゼロ埋め)を
忘れたか、受信バイト数を記憶するかのどちらかを忘れた、とも取れますね。
というわけで、「受信長を覚えておく」という第三の選択肢も挙げてみます。


返信引用
こまったちゃん
 こまったちゃん
(@こまったちゃん)
ゲスト
結合: 18年前
投稿: 39
Topic starter  

アドバイスありがとうございます。

>char Company[6]={あいうえお};
と簡単に書いてしまいましたが、本来は
ヘッダーファイルで#define BUFF 51として。
char Company[BUFF];
memset(Company, '\0', sizeof(Company));
としています。省略してしまい申し訳ありませんでした。

sprintf(buff, %s\r\n%s\r\n%s\r\n, Company, SName, FName);
とやるのが一般的なんですね。初めてなんで勉強になります。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

> sprintf(buff, %s\r\n%s\r\n%s\r\n, Company, SName, FName);
> とやるのが一般的なんですね。初めてなんで勉強になります。
「\r\n」はデミリタです。
データの区切りに使います。
Company, SName, FNameは連続したひとかたまりのデータですよね。
ですから、
sprintf(buff, %s%s%s\r\n, Company, SName, FName);
でいいと思います。
yoh2さんのご意見は、こうだと思います。

HEADER XXXX\0
sprintf(buff, %s\r\n%s%s%s\r\n, HEADER, Company, SName, FName);

HEADERの内容は任意に考えてください。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

> sprintf(buff, %s%s%s\r\n, Company, SName, FName);
これは、それぞれの情報長が固定で足りない部分は何かでうめて送信する場合ですよね。
そうじゃないと各項目の区切りが分からないと言う最初の状態に結局戻ってしまう。
各項目が可変長なら項目毎にデリミタを入れる必要がありますが、
項目毎のデリミタと一塊毎のデリミタを同じにするかどうかは設計しだいでしょう。
データの長さのばらつきが非常に大きいなら各項目毎のデータ長も送信するように
する方法もあります。
例えば、
[データ開始識別子][データ長(short int)][文字列データ(データ長分)][データ長(short
int)][文字列データ(データ長分)][データ長(short int)]・・・[データ終了識別子]
というような設計の仕方もあります。
データ開始識別子とデータ終了識別子には基本的にデータとしてありえない組み合わせを
使うのが一般的だと思います。

実際の話、こういった電文フォーマットは自分たちが送受信するのに便利なように
決めるのが普通です。で、固定長の方が受信時の判定がやりやすいので
データ長のばらつきが少ないのなら固定長にしてしまった方が処理が楽です。
この辺は何をやりたいか次第なので実際にはやりたい事を理解している本人じゃないと
本当に適切なフォーマットは決められないと思います。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

> これは、それぞれの情報長が固定で足りない部分は何かでうめて送信する場合
ですよね。
> そうじゃないと各項目の区切りが分からないと言う最初の状態に結局戻ってしまう。
文字列をデータとして扱う場合は特に固定長で考えたほうがいいと思いました。

> 各項目が可変長なら項目毎にデリミタを入れる必要がありますが、
なるほど、可変長の時は必要ですね。
でも、パケット通信で可変長は厄介ですよね。
RS-232C/RS-422のように連続したデータを取得するなら別ですが....


返信引用
こまったちゃん
 こまったちゃん
(@こまったちゃん)
ゲスト
結合: 18年前
投稿: 39
Topic starter  

みなさん本当にありがとうございます。

ソケット通信の流れも最初よりはわかりました。
フォーマットを設計してからプログラムですね。

また解らない事がありましたら質問しますので
よろしくお願い致します。


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

返信する

投稿者名

投稿者メールアドレス

タイトル *

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