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

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

新規登録して質問してみよう
ただいま回答率
85.35%
ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

C++

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

Q&A

1回答

1467閲覧

TCP read()でデータが抜ける?(C++)

veratail

総合スコア2

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

C++

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

0グッド

0クリップ

投稿2020/10/21 04:31

編集2020/10/21 09:58

C++11でTCPソケット通信をしています。
運用中に以下のエラーが発生しました。

発生している問題

read()をdo~while中に記述し、返り値が負のときはエラー出力、
0のときはもう一度read()を繰り返すことで、
返り値が正ならば次に進むようにしています。

例えば、1,2,3等の値が取りたいデータとすると、
12345・・・(以下略)
と続けて取得したいのに、
1か2が抜けてしまうという問題が、数時間に1度程度、不定期に発生します。
(ほとんどの場合は正しい結果になる)

1と2に相当するデータは、仕様で決まっている値で、
1は特定の条件、2は読み取るバイト数の決定に必要となります。

該当のソースコード

C++

1//構造体、共用体定義抜粋 2struct CommandSet{ 3 uint8_t command_set[42] 4} 5 6union ControlSet{ 7AngleSet angle; 8CommandSet command; 9}; 10 11struct SocketSet{ 12 int sockFromSvr; 13}

C++

1SocketSet sock; 2void *SvrListner(void *arg) { 3 4 std::unique_ptr<ControlSet> CommandsFromSvr(new ControlSet); 5 6 while (true) { 7 ssize_t rec; 8 do { 9 rec = read(sock.sockFromSvr, &CommandsFromSvr.get()->command.command_set[0], 1); 10  if (rec < 0) { 11   throw SysCallError(errno); 12 } 13 } while (rec <= 0); 14 15 switch ((int)CommandsFromSvr.get()->command.command_set[0]) { 16   case 1: { 17 ssize_t rec; 18    do { 19     rec = read(sock.sockFromSvr, &CommandsFromSvr.get()->command.command_set[0], 1); 20 21 if (rec < 0) { 22 throw SysCallError(errno); 23  } 24    } while (rec <= 0); 25 26 size_t len = 0; 27 28 do { 29 ssize_t rec = read(sock.sockFromSvr , &CommandsFromSvr.get()->command.command_set[len + 1], (int)CommandsFromSvr.get()->command.command_set[0] * 2) - len); 30 if (rec < 0) { 31                       throw SysCallError(errno); 32 } else if (rec == 0) { 33                    //エラーメッセージ 34 } 35 len += rec; 36  } while (len < ((int)CommandsFromSvr.get()->command.command_set[0] * 2)); 37 38                 break; 39              } 40 41     case 2:{ 42                 //case 2 の処理 43                 break; 44              } 45     } 46 47 } 48 return nullptr; 49} 50 51int NewTCPSocketConnectingTo(char const *addr, uint16_t port) 52 throw (SysCallError) { 53 struct sockaddr_in server; 54 memset(&server, 0, sizeof server); //struct sockaddr_in serverの初期化 55 56 /*接続先指定用構造体の準備*/ 57 server.sin_family = AF_INET; 58 server.sin_port = htons(port); 59 60 inet_pton(AF_INET, addr, &server.sin_addr.s_addr); 61 62 /*サーバに接続*/ 63 int sock = socket(AF_INET, SOCK_STREAM, 0); /*ソケットの作成*/ 64 65 //sockから負の場合のエラー処理 66 if (sock < 0) { 67 throw SysCallError(errno); 68 } 69 70 // connectの戻り値のチェックとエラー処理 71 int conn = connect(sock, (struct sockaddr *) &server, sizeof(server)); 72 if (conn < 0) { 73 throw SysCallError(errno); 74 } 75 return sock; 76 77} 78 79int main() { 80 sock.sockFromSvr = NewTCPSocketConnectingTo("***.***.***.***", 81 PortNum); 82 83 Thread threadForSvr(SvrListener, "SvrListener"); 84 threadForSvr.Run(nullptr); 85 86 threadForSvr.Join(); 87 88 return 0; 89}

試したこと

当初は、最初の2バイトを読む処理にdo~whileを書いておらず、
返り値が0になった場合を考慮していませんでした。
そのことが原因と考え、上記のようにしたのですが、
解決には至りませんでした。

おかしい点など、ご指摘いただければありがたいです。

開発環境

Linux distribution: 2.6.32-754.3.5.el6.i686
OS: Cent OS 6.10
IDE: Eclipse Oxygen.3a Release(4.7.3a)
gcc: 5.3.1

気になる質問をクリップする

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

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

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

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

yuki23

2020/10/21 07:07

bufの定義は何ですか?
veratail

2020/10/21 07:15

yuki23様、追記依頼をありがとうございます。 uint8_t型の配列です。
yuki23

2020/10/21 09:00

bufの定義を聞いた理由は、y_waiwaiさんと同じ疑いを持ったからです。 ソースコードは、問題が再現する最小限のものを貼ってください。このような中略だらけの不完全なコードでは何もわかりません。これは人に質問するときだけでなく、自分でデバッグするときにも必須の作業です。
veratail

2020/10/21 10:02

yuki23様、コメントをありがとうございます。 ご指摘、勉強になります。お時間を無駄にしてしまい、申し訳ありませんでした。 問題が再現する最小限のものを貼ったつもりなのですが、いかがでしょうか。 よろしければ、再度ご確認いただければ幸いです。
otn

2020/10/21 15:49

動かしたコードのコピペでないものは、質問に載せる意味が無いです。
veratail

2020/10/22 03:02 編集

otn様、コメントをありがとうございます。 どうやら、yuki23様のご指摘も含め、マナーを理解していなかったようです。 貴重なお時間を無駄にしてしまい、申し訳ありませんでした。 「問題が再現する最小限」の、「動かしたコードのコピペ」を質問に載せるようにいたします。 少し準備の時間が必要ですので、見苦しいままとなりますが、しばらくそのままとさせていただきます。
guest

回答1

0

rec = read(socket, buf[0], 1);

とありますが、なにかおかしいです
read関数の第二引数は、バッファのアドレスが入るはずですが、そうなってないようです。
bufの定義はどうなってるでしょうか

投稿2020/10/21 07:15

y_waiwai

総合スコア88042

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

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

veratail

2020/10/21 09:00 編集

y_waiwai様、ご回答ありがとうございます。 こちらに記載する際の誤記でした。 安易にbufと省略しておりましたが、省略しない形に修正しました。 データ抜けと思われる事象は、read関数の第二引数に、バッファのアドレスが入る形で発生しております。 恐れ入りますが、再度ご確認いただければ幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問