C言語のソケットプログラムについて送受信の部分について質問いたします
たとえば送信したいものが
実行処理のタイプ(char)
int 変数何個か(charによりいくつ送信するか変わります)
文字列配列(可変長)
だった場合送受信の部分は簡易コードで書くと
送信側
send(socket,char , sizeogf(char),0)
send(socket,int , sizeogf(int),0)
send(socket,int , sizeogf(int),0)
....
send(socket,int , sizeogf(int),0)
send(送信する文字列配列のサイズをintで送信,sizeof(int) )
for loop
send(文字列送信)
受信側
recv(soc,char,sizeof(char),0)
swithch で分岐して
recv(int ,sizeof(int))
recv(int ,sizeof(int))
......
recv(int ,sizeof(int))
recv(可変長文字列のサイズを受信)
bufを用意
recv(buf,sizeof(buf))
のような形で書くのがいいのでしょうか?
それとも, なにか決まりがあるのでしょうか?
たとえばchar型の文字列につめて一気に送信するとか..
このジャンルのプログラムを組む機会があまりなかったので困っています、お力をお貸しください
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
もし、本格的に作るなら、プロトコル仕様書を書くことから始めることを勧めます。どのデータをどのように渡すのかを決めれば、それを実装するだけで終わります。仕様書の書き方は他のプロトコルのRFCを参考にするといいと思います。(嫌になるほど細かいですけど、それだけ注意が必要という事です)
他の方も書いていますが、C上のデータをそのまま渡すことは問題があります。
- char以外はバイトオーダーが実装依存です。(charは常に1バイトなので気にする必要はありません。)
- structはalignmentが実装依存です。
sturct st {char a; int b;}
が5バイトなのか8バイトなのかはコンパイラによって異なります。 - そもそもint等のサイズは実装依存です。特にlong intは同じx86_64環境の64bitでもWindowsとLinuxでは違います。
上記のようにCには実装依存が数多くあるため、OS、CPUアーキテクチャ、コンパイラ、コンパイラのオプション、全てを同じにしないと送信先で同じデータを復元できるかは保証できません。
なお、パケット損失やパケット順についてですが、
- TCPはパケット損失を自動で検知し、パケット順も自動で並び替えてくれます。TCPを使う限り、関数がエラーになるかさえ気をつければ、データロストや順番入れ替えは気にしなくてもいいです。(ただし、常にエラーになる可能性だけは、気をつけるべきです)
- UDPはパケット損失なんて見てくれないし、パケット順も保証されません。関数にエラーが無くても、正しいかどうかは別途確認する必要があります。
TCPだけを使えば問題ありませんが、マルチキャストにしたい場合や多少損失があっても速度を重視したい(ストリーミング配信など)場合はUDPを使う必要があります。
投稿2015/09/01 14:33
総合スコア21739
0
データの発生の都度sendしているのであれば、送信はそれで問題ないでしょう。
そうじゃなくて、intが配列に入っているなら、個別にsendするのでなく配列のままでサイズを指定してsendするのがいいと思います。
可変長文字列のcharについても同様。
受信についても、1つずつrecvするのでなく、データの繰り返し個数が決まった段階で配列にまとめて受信することが可能ならその方が良いでしょう。
また、受信については、指定した長さのデータ全体が受信できるとは限らないと言うことはご存じでしょうか。
4バイト受信しようとしても、2バイトしか受信せずにrecvから戻ってくるかもしれない。
recvの戻り値をチェックして、目的長に足りなければ再度recvする必要があります。
man recvの下記の部分を参照。
これらの三つのルーチンはいずれも、成功した場合にはメッセージの長さを返 す。メッセージが長過ぎて指定されたバッファに入り切らなかった場合には 、 メ ッセージを受信したソケットの種類によっては余分のバイトが捨てられるか もしれない。 (中略) これらの受信用のコールは、受信したデ ータのサイズが要求したサイズに達するまで待つのではなく、何らかのデー タ を 受信すると復帰する (受信されるデータの最大サイズは要求したサイズであ る)。 (中略) これらのコールは受信したバイト数を返す。エラーの場合は -1 を返す。接続 先が正しくシャットダウンを実行した場合は、返り値は 0 となる。
つまり、4バイト受信するためには最大4回recvを行う必要があるかもしれません(※)。
また、返値0を判断して、データが来るはずであったのなら、異常処理(TCPセッションが切れた)を行う必要があります。
※のあたりの手を抜くには、fdopenしてfreadを使う手もあるかと思います。
freadなら、途中でセッションが終了しない限り指定した長さを読めるはずです(ドキュメントにはそう書いてある)。つまり、返値が指定した読み取り長以下ならセッションが切れたということで、異常処理を行う。
intのバイトオーダーについては、プロトコルでリトルエンディアンかビッグエンディアンかを決めておけばどちらでも良いです。オープンなプロトコル(ハードウェアやOSを限定しないプロトコル)では、他の方の回答の通りビッグエンディアンが使われます。特定環境限定のプロトコルなら、その環境に最適なオーダーが使われることも多いでしょう。
また、今回の質問の範囲外だと思いますが、ソケットの切断後の再接続での続行を考えているなら、これも他の方の書いている通りに、前回のTCPセッションでどこまで送れたのかを確認する仕組みが必要です。送受信双方で、どこまで送れたのかの認識がずれている可能性がある。基本的には、受信側から「どこまで受信できたのか」を通知します。
例えば、HTTPプロトコルやFTPプロトコルだと、「データの〇〇バイト目から送れ」という仕組みが存在しますので、データを受信中にセッション断が発生したら、続きから受信できます。
※についての補足。
一応、recvの第4引数のflagにこういうのがあるので、使えるのかもしれません。
MSG_WAITALL (Linux 2.2 以降) このフラグは、要求した量いっぱいのデータが到着するまで、操作を停 止 (block) するよう要求する。但し、シグナルを受信したり、エラ ー や切断 (disconnect) が発生したり、次に受信されるデータが異なる型 だったりした場合には、要求した量よりデータが少なくても返ることが ある。
投稿2015/09/01 13:37
総合スコア85949
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
char型やint型のシンプルなデータを送るだけならば、質問に書かれたやり方で十分だと思います。
ただし数値型データを送るときは、バイトオーダー変換をつけた方が無難です。送信側受信側ともに、IAアーキテクチャしかないのであれば問題ありませんが、一応約束事としては、通信はネットワークバイトオーダー(ビッグエンディアン)とホストバイトオーダー(IAならリトルエンディアン)の変換処理が必要です。
もう少しデータが複雑化して、構造体や配列にして送るならば、データ部のデータ長をヘッダーに持たせたりすることが必要になります。
通信プログラムは、絶えずデータのロストを考慮しなければならないので、受信側は、すべてのデータが取得できたことを確認する必要がありますし、送信側はリトライが必要になります。(2重受信の対策も必要です)
さらにデータをそのまま送ると、送信受信ともに送受信データの変更に弱くなってしまうので、XMLやJSONといったフォーマットで送受信することもあります。
投稿2015/09/01 12:36
総合スコア1040
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/09/01 14:01 編集
2015/09/01 15:51
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。