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

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

新規登録して質問してみよう
ただいま回答率
85.54%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

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

Q&A

解決済

2回答

13736閲覧

C++での通信処理におけるデータの取りこぼし

Unity-chan

総合スコア20

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

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

0グッド

1クリップ

投稿2017/01/12 07:45

###前提・実現したいこと
カメラから取得したRGBデータをネットワーク経由で他のPCへ送信するプログラムを作成しています.
現在通信にはWinsock2でUDP通信を使用しています.

###発生している問題
取得した画素情報を逐次送信した場合,recvfrom()でデータを取りこぼす.

###該当のソースコード

C++

1// Send.cpp 2#include "SocketCom.h" 3#define HEIGHT 480 4#define WIDTH 640 5static char pixelData[3][HEIGHT][WIDTH]; //送信したいピクセルデータ 6 7int main(void){ 8 InitSendSocketCommunication("127.0.0.1", 11111); 9 10 ----- データ格納及びシーケンス番号を付与する処理 ----- 11 12 // 送信処理 13 for (int i = 0; i < HEIGHT ; i++){ 14 SendCharToPartner(pixelData[0][i], sizeof(pixelData[0][i])); 15 SendCharToPartner(pixelData[1][i], sizeof(pixelData[1][i])); 16 SendCharToPartner(pixelData[2][i], sizeof(pixelData[2][i])); 17 } 18 19 return 0; 20}

C++

1// Receive.cpp 2#include "SocketCom.h" 3#define HEIGHT 480 4#define WIDTH 640 5static char pixelData[3][HEIGHT][WIDTH]; //受信したピクセルデータ 6 7int main(void){ 8 InitReceiveSocketCommunication("127.0.0.1", 11111); 9 10 ----- データ格納及びシーケンス番号を付与する処理 ----- 11 12 // 受信処理 13 for (int i = 0; i < HEIGHT ; i++){ 14 ReceiveDataAsChar(pixelData[0][i], ARRAY_SIZE(pixelData[0][i])); 15 ReceiveDataAsChar(pixelData[1][i], ARRAY_SIZE(pixelData[1][i])); 16 ReceiveDataAsChar(pixelData[2][i], ARRAY_SIZE(pixelData[2][i])); 17 18 ----- シーケンス番号を元に対応する配列に格納する処理 ----- 19 } 20 21 return 0; 22}

C++

1// SecketCom.h 2#define UDP_MAX 65507 // UDP通信にて一度に送信することができる最大サイズ 3#define ARRAY_SIZE(arr) ( sizeof(arr) / sizeof(arr[0]) ) // 配列の要素数を返すスクリプト 4 5#include <string> 6using std::string; 7 8// Description: 9// 送信する変数型一覧. 10typedef enum Datatype{ 11 TYPE_INT, 12 TYPE_DOUBLE, 13} Datatype; 14 15// IPアドレスとポート番号で初期化する 16extern bool InitSendSocketCommunication(string IPAdress,int portnumPartner); 17extern bool InitReceiveSocketCommunication( string IPAdress,int portnumPartner); 18extern bool FinalSocketCommunication(); 19extern bool SendStringToPartner(string sendData); 20extern bool SendValueToPartner(void *sendData,int arraySize,Datatype datatype); 21extern bool ReceiveDataAsChar( char *receivedata,int dataSize); 22extern bool ReceiveDataAsValue( void *data,int dataSize,Datatype datatype);

C++

1// SocketCom.cpp 2//============================================================================= 3// include / using / define 4//============================================================================= 5 6// ソケット通信 7#include <winsock2.h> 8// string 9#include "SocketCommunication.h" 10using std::to_string; 11// 文字列出力 12#include <iostream> 13using std::cout; 14using std::endl; 15 16#define STR(var) #var 17#define SEPARATOR "/" 18 19 20//============================================================================= 21// variable 22//============================================================================= 23 24static SOCKET g_sockSend, g_sockReceive; // 送受信の通信プロトコル設定 25static struct sockaddr_in g_addrSend, g_addrReceive; // 送受信のアドレス,ポート設定 26static bool isInitialized = false; 27static int g_socketError = 0; 28static bool g_isBind = false; 29static int sockaddr_in_size; 30 31 32//============================================================================= 33// Static Function 34//============================================================================= 35 36// WinSockのバージョンを指定 37static bool InitWSAData(){ 38 WSADATA wsaData; 39 int error; 40 41 error = WSAStartup(MAKEWORD(2, 0), &wsaData); 42 if (error != 0){ 43 cout << "Winsock WSAStartup Error (SocketCommunication.cpp > InitWSAData())" << endl; 44 cout << STR(error) << endl; 45 46 return false; 47 } 48 isInitialized = true; 49 return true; 50} 51 52//============================================================================= 53// Initialize / Finalize Functions 54//============================================================================= 55 56// 送信の初期化 57bool InitSendSocketCommunication(string IPAdress, int portnumPartner){ 58 59 if (!isInitialized) 60 if (!InitWSAData()) 61 return false; 62 63 // 送信のプロトコルを決める 64 g_sockSend = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 65 g_addrSend.sin_family = AF_INET; 66 g_addrSend.sin_port = htons(portnumPartner); 67 g_addrSend.sin_addr.S_un.S_addr = inet_addr(IPAdress.c_str()); 68 69 return true; 70} 71 72// 受信の初期化 73bool InitReceiveSocketCommunication(string IPAdress, int portnumRecv){ 74 if (!isInitialized) 75 InitWSAData(); 76 77 // 送信のプロトコルを決める 78 g_sockReceive = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 79 g_addrReceive.sin_family = AF_INET; 80 g_addrReceive.sin_port = htons(portnumRecv); 81 g_addrReceive.sin_addr.S_un.S_addr = INADDR_ANY; 82 sockaddr_in_size = sizeof(struct sockaddr_in); 83 84 if (!g_isBind){ 85 g_socketError = bind(g_sockReceive, (struct sockaddr *)&g_addrReceive, sizeof(g_addrReceive)); 86 if (g_socketError != 0){ 87 cout << "Port bind failture! (SocketCommunication.cpp > ReceiveDataAsChar() > bind())" << endl; 88 return false; 89 } 90 g_isBind = true; 91 } 92 char val; 93 int len; 94 95 return true; 96} 97 98// ソケット通信の終了 99bool FinalSocketCommunication(void){ 100 closesocket(g_sockSend); 101 WSACleanup(); 102 103 return true; 104} 105 106 107//============================================================================= 108// Send / Receive Functions 109//============================================================================= 110 111// 文字列の送信 112bool SendStringToPartner(string sendData){ 113 // UDPの限界を超えていないかチェック 114 if (sendData.size() > UDP_MAX){ 115 cout << "Data size is too large! (SocketCommunication.cpp > SendStringToPartner() > string sendData)" << endl; 116 return false; 117 } 118 119 g_socketError = sendto(g_sockSend, sendData.c_str(), sendData.size(), 0, (struct sockaddr *)&g_addrSend, sizeof(g_addrSend)); 120 if (g_socketError == -1){ 121 cout << "Data send failture! (SocketCommunication.cpp > SendStringToPartner() > sendto())" << endl; 122 return false; 123 } 124 125 return true; 126} 127 128// 数値の送信(文字列へ変換して送る) 129bool SendValueToPartner(void *sendData, int arraySize, Datatype datatype){ 130 string buffer; 131 int *bufInt; 132 double *bufDouble; 133 134 switch (datatype){ 135 case TYPE_INT: 136 bufInt = (int*)sendData; 137 138 for (int j = 0; j < arraySize; j++){ 139 buffer += to_string(*(bufInt + j)); 140 buffer += SEPARATOR; 141 } 142 break; 143 case TYPE_DOUBLE: 144 bufDouble = (double*)sendData; 145 146 for (int j = 0; j < arraySize; j++){ 147 buffer += to_string(*(bufDouble + j)); 148 buffer += SEPARATOR; 149 } 150 break; 151 } 152 153 if( !SendStringToPartner(buffer) ) 154 return false; 155 156 return true; 157} 158 159// charデータの受信 160bool ReceiveDataAsChar(char *data, int dataSize){ 161 // UDPの限界を超えていないかチェック 162 if (dataSize > UDP_MAX){ 163 cout << "Data size is too large! (SocketCommunication.cpp > ReceiveDataAsChar() > int dataSize)" << endl; 164 return false; 165 } 166 167 memset(data, 0, dataSize); 168 169 g_socketError = recvfrom(g_sockReceive, data, 1284, 0, (struct sockaddr *)&g_addrReceive, &sockaddr_in_size); 170 if (g_socketError < 0){ 171 cout << "Data receive failture! (SocketCommunication.cpp > ReceiveDataAsChar() > recv())" << endl; 172 cout << "ERROR_CODE:" << WSAGetLastError() << endl; 173 return false; 174 } 175 176 return true; 177} 178 179// 数値データの受信 180bool ReceiveDataAsValue(void *data, int dataSize, Datatype datatype){ 181 char buffer[UDP_MAX]; 182 static char *p; //正直謎ポインタ(for strtok_s()) 183 184 // とりあえずchar型として受け取る 185 if (!ReceiveDataAsChar(buffer, sizeof(buffer))) 186 return false; 187 188 // doubleとかはcharとデータサイズが違うから,数値データ一つ毎にSEPARATORをはさんで送る 189 switch (datatype){ 190 case TYPE_INT: 191 *((int*)data) = atof( strtok_s( buffer, SEPARATOR, &p ) ); 192 for (int i = 1; i < dataSize; i++) 193 *((int*)data + i) = atof(strtok_s(NULL, SEPARATOR, &p)); 194 break; 195 case TYPE_DOUBLE: 196 *((double*)data) = atof( strtok_s( buffer, SEPARATOR, &p ) ); 197 for (int i = 1; i < dataSize; i++) 198 *((double*)data + i) = atof(strtok_s(NULL, SEPARATOR, &p)); 199 break; 200 } 201 202 return true; 203}

###試したこと
送信のforループの中に無意味な処理("for (int h = 0; h < 1000000; h++)")を追加して送信の間隔を空けた場合,取りこぼすことなく受信することができた.しかし速度を重視してUDPを採用したので,可能であれば無意味な処理を使用せず実行したいと考えている.

###補足情報(言語/FW/ツール等のバージョンなど)
IDE
Visual Studio 2013 Express for Windows Desktop

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

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

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

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

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

guest

回答2

3

速度を重視する代わりに取りこぼしが起きるのがUDPです。UDPを使う以上、取りこぼしを防ぐのは無理だと思います。もし取りこぼしに対する検証処理を書くなら、最初からTCPを使うほうが良いと思います。

投稿2017/01/12 07:52

masaya_ohashi

総合スコア9206

takito, katsuya141, Y.H.👍を押しています

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

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

Unity-chan

2017/01/12 08:38

回答ありがとうございます。 UDPを用いているので多少の取りこぼしがあるのは許容できます.しかしWireSharkでパケットを確認すると,受信自体はできているように見えるにも関わらずrecvfrom()を実行した場合に良くても10%程のデータしか受け取ることが出来ませんでした. 今まで取りこぼしの主な原因は通信経路上のパケットロスと思っていたのですが,こういったロスも多いのでしょうか….その場合だとTCPにした方がいいかもしれません.
masaya_ohashi

2017/01/12 08:50

んー…そこまで詳しい調査をしたことはないのでどこに原因があるかは不明です。 UDPは下手な鉄砲数撃ちゃ当たるで、TCPは必中スナイパーライフルのようなもの、とだけ認識して使っていたので、詳しくはわかりません。UDPは多少間が抜けても構わないもの、TCPは絶対抜け落ちてはならないものに使うので、画像等の1つのバイナリデータを送信するならTCPを使うのが無難かと思います。
guest

2

ベストアンサー

他回答へのコメントより:

UDPを用いているので多少の取りこぼしがあるのは許容できます.しかしWireSharkでパケットを確認すると,受信自体はできているように見えるにも関わらずrecvfrom()を実行した場合に良くても10%程のデータしか受け取ることが出来ませんでした.

質問者さんが言及している通り、UDPはあらゆるレイヤでデータグラム欠損が起きうる通信プロトコルです。実ネットワーク上でのパケット・ロス発生以外にも、NIC内部で破損検知したデータグラムを破棄したり、ネットワークスタック内部の送受信バッファ不足によりデータグラムを破棄することもあります。

SO_SNDBUFSO_RCVBUFにより送受信バッファサイズを大きくすれば、UDPデータグラムの取りこぼしが多少は減る可能性があります。

送信のforループの中に無意味な処理("for (int h = 0; h < 1000000; h++)")を追加して送信の間隔を空けた場合

UDP送信処理という観点からは、上記のような微妙なタイミング制御には意味があります。データグラム欠損を極力避けるには、UDP送信側はなるべく一定間隔でのデータ送信を行うよう制御し(バースト的な送出処理は避ける)、UDP受信側は可能な限り最速でネットワークスタックからデータを取り出す(recvfromとその後の解析処理を分離するなど)必要があります。

しかし速度を重視してUDPを採用したので,可能であれば無意味な処理を使用せず実行したいと考えている.

(気持ちは分かりますが)UDP通信路全体でみると全くの逆効果です。送信側だけの都合で最速処理すると、既に説明したようなデータグラム欠損があちこちで生じてしまいます。

投稿2017/01/13 03:02

編集2017/01/13 03:10
yohhoy

総合スコア6187

YsMana, masaya_ohashi👍を押しています

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

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

Unity-chan

2017/01/27 15:38

返答が遅くなって申し訳ありません! 以下のプログラムを追加してバッファを大きくしたところ正常に動作することを確認できました。ありがとうございます。 int recvBuffer setsockopt(g_sockReceive, SOL_SOCKET, SO_RCVBUF, (char *)&recvBuffer, OptLen);
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.54%

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

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

質問する

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

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

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