質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.53%
Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

5回答

43422閲覧

ソケットプログラムでの送信側と受信側でのプログラミング方法について

退会済みユーザー

退会済みユーザー

総合スコア0

Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2015/09/01 11:56

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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

もし、本格的に作るなら、プロトコル仕様書を書くことから始めることを勧めます。どのデータをどのように渡すのかを決めれば、それを実装するだけで終わります。仕様書の書き方は他のプロトコルのRFCを参考にするといいと思います。(嫌になるほど細かいですけど、それだけ注意が必要という事です)

他の方も書いていますが、C上のデータをそのまま渡すことは問題があります。

  1. char以外はバイトオーダーが実装依存です。(charは常に1バイトなので気にする必要はありません。)
  2. structはalignmentが実装依存です。sturct st {char a; int b;}が5バイトなのか8バイトなのかはコンパイラによって異なります。
  3. そもそもint等のサイズは実装依存です。特にlong intは同じx86_64環境の64bitでもWindowsとLinuxでは違います。

上記のようにCには実装依存が数多くあるため、OS、CPUアーキテクチャ、コンパイラ、コンパイラのオプション、全てを同じにしないと送信先で同じデータを復元できるかは保証できません。

なお、パケット損失やパケット順についてですが、

  1. TCPはパケット損失を自動で検知し、パケット順も自動で並び替えてくれます。TCPを使う限り、関数がエラーになるかさえ気をつければ、データロストや順番入れ替えは気にしなくてもいいです。(ただし、常にエラーになる可能性だけは、気をつけるべきです)
  2. UDPはパケット損失なんて見てくれないし、パケット順も保証されません。関数にエラーが無くても、正しいかどうかは別途確認する必要があります。

TCPだけを使えば問題ありませんが、マルチキャストにしたい場合や多少損失があっても速度を重視したい(ストリーミング配信など)場合はUDPを使う必要があります。

投稿2015/09/01 14:33

raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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

otn

総合スコア84264

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

char型やint型のシンプルなデータを送るだけならば、質問に書かれたやり方で十分だと思います。

ただし数値型データを送るときは、バイトオーダー変換をつけた方が無難です。送信側受信側ともに、IAアーキテクチャしかないのであれば問題ありませんが、一応約束事としては、通信はネットワークバイトオーダー(ビッグエンディアン)とホストバイトオーダー(IAならリトルエンディアン)の変換処理が必要です。

もう少しデータが複雑化して、構造体や配列にして送るならば、データ部のデータ長をヘッダーに持たせたりすることが必要になります。
通信プログラムは、絶えずデータのロストを考慮しなければならないので、受信側は、すべてのデータが取得できたことを確認する必要がありますし、送信側はリトライが必要になります。(2重受信の対策も必要です)

さらにデータをそのまま送ると、送信受信ともに送受信データの変更に弱くなってしまうので、XMLやJSONといったフォーマットで送受信することもあります。

投稿2015/09/01 12:36

shanxia

総合スコア1038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/09/01 12:42

>もう少しデータが複雑化して、構造体や配列にして送るならば、データ部のデータ長をヘッダーに持たせたりすることが必要になります。 みなさん回答ありがとうございます この部分について疑問を持ちました、もし構造体の配列を送りたい場合どのようなsend ,recvの関係にしたらいいのかわからないのです。 たとえばunsigned int 型の変数4つをもつ構造体を一回送るにはどのような仕組みをすればいいのでしょうか struct map { unsigned int 4つ } となった場合、 struct map str; send (str ,sizeof(str)) ではだめなのでしょうか?ヘッダ部分とはなんでしょうか?
shanxia

2015/09/01 14:01 編集

ヘッダ部分とは、データ部分が何であるか、どれくらいの長さかを説明するものです。 send ( str, sizeof(str) ) として送った場合、送信側受信側ともに、同じレイアウトの構造体を1つだけ使うという決まりであれば問題ありません。 しかしそれでは、変更に非常に弱い構造になってしまいます。 そこで、データとは別に、共通で使用するヘッダー情報の構造体を用意して、それを一緒に送ります。 たとえば、以下の様に構造体を定義します。 struct ComHeader { char version;/* バージョン */ char type;/* 処理種別(構造体種別) */ int len;/* データ長 */ char reserve[2];/* リザーブエリア */ } struct SendData { struct ComHeader com; struct map* pMap; } 使用する場合は、send()にSendDataを使用します。 ComHeaderでは一応3つほどメンバを用意しています。 たとえば受信側は、次のような判断を行います。 1.versionを確認して、未対応バージョンの場合は処理しない。 2.typeを見て、データ部の構造体が、mapであることを判断する。 3.データ長を見て、pMapが何個あるかを判断する。 受信側は、最初のComHeaderがある先頭8バイトのデータを確認するだけで、その後のデータが何かを判断します。そのため、map以外を送る場合は同じように構造体を使用すれば、受信側の判断処理は、同じ仕組みでできます。 なお、ComHeaderのreserve[2]は、境界整列のためにおいています。 32bitOSの場合は、構造体サイズは4の倍数で確保されるため、reserve[2]があってもなくても、ComHeaderのサイズは8バイトになります。 こうしておくと、メモリサイズの計算間違いが少なくなります。
otn

2015/09/01 15:51

ヘッダというのは、あなたのプログラムだと、 > 実行処理のタイプ(char) と、 > 送信する文字列配列のサイズをintで送信 の部分ですね。後続のデータ本体の種類や長さを示すもの。
guest

0

ちょっと説明が長いですが、下記サイトの「ソケット入門」を参考にされてはどうでしょう?
プログラム講座
Cのサンプルプログラムもあるようです。

投稿2015/09/01 12:30

cateye

総合スコア6851

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

決まりという訳ではないでしょうが、一般的には送受信に使用するバッファ(固定長のchar型配列)を用意しておき、それにデータを詰めて送受信していますね。(ご参考)

投稿2015/09/01 12:17

pi-chan

総合スコア5936

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問