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

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

ただいまの
回答率

90.38%

  • C++

    4653questions

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

  • プログラミング言語

    773questions

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

  • Socket.IO

    224questions

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

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

受付中

回答 5

投稿

  • 評価
  • クリップ 1
  • VIEW 23K+
退会済みユーザー

退会済みユーザー

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型の文字列につめて一気に送信するとか.. 


このジャンルのプログラムを組む機会があまりなかったので困っています、お力をお貸しください
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+1

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/09/01 21:42

    >もう少しデータが複雑化して、構造体や配列にして送るならば、データ部のデータ長をヘッダーに持たせたりすることが必要になります。

    みなさん回答ありがとうございます
    この部分について疑問を持ちました、もし構造体の配列を送りたい場合どのようなsend ,recvの関係にしたらいいのかわからないのです。
    たとえばunsigned int 型の変数4つをもつ構造体を一回送るにはどのような仕組みをすればいいのでしょうか
    struct map {
    unsigned int 4つ
    }
    となった場合、
    struct map str;

    send (str ,sizeof(str))
    ではだめなのでしょうか?ヘッダ部分とはなんでしょうか?

    キャンセル

  • 2015/09/01 22:58 編集

    ヘッダ部分とは、データ部分が何であるか、どれくらいの長さかを説明するものです。
    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バイトになります。
    こうしておくと、メモリサイズの計算間違いが少なくなります。

    キャンセル

  • 2015/09/02 00:51

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

    キャンセル

+1

データの発生の都度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) が発生したり、次に受信されるデータが異なる型
             だったりした場合には、要求した量よりデータが少なくても返ることが
             ある。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

もし、本格的に作るなら、プロトコル仕様書を書くことから始めることを勧めます。どのデータをどのように渡すのかを決めれば、それを実装するだけで終わります。仕様書の書き方は他のプロトコルの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を使う必要があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C++

    4653questions

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

  • プログラミング言語

    773questions

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

  • Socket.IO

    224questions

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