非常に奇妙なバグがあります。
TCP/IPを介して通信する2つのアプリケーションがあります。
アプリケーションAはサーバーであり、アプリケーションBはクライアントです。
アプリケーションAは、100ミリ秒ごとに一連のfloat値をアプリケーションBに送信します。
バグは次のとおりです。アプリケーションBによって受信されたfloat値の一部が、アプリケーションAによって送信された値と同じでない場合があります。
当初、イーサネットまたはTCP / IPドライバに問題があると思いました(ある種のデータ破損)。次に、他のWindowsマシンでコードをテストしましたが、問題は解決しませんでした。
次に、Linux(Ubuntu 10.04.1 LTS)でコードをテストしましたが、問題はまだあります!!!
値は、送信される直前と受信された直後にログに記録されます。
コードは非常に単純です。メッセージプロトコルには、次のような4バイトのヘッダーがあります。
//message header
struct MESSAGE_HEADER {
unsigned short type;
unsigned short length;
};
//orientation message
struct ORIENTATION_MESSAGE : MESSAGE_HEADER
{
float azimuth;
float elevation;
float speed_az;
float speed_elev;
};
//any message
struct MESSAGE : MESSAGE_HEADER {
char buffer[512];
};
//receive specific size of bytes from the socket
static int receive(SOCKET socket, void *buffer, size_t size) {
int r;
do {
r = recv(socket, (char *)buffer, size, 0);
if (r == 0 || r == SOCKET_ERROR) break;
buffer = (char *)buffer + r;
size -= r;
} while (size);
return r;
}
//send specific size of bytes to a socket
static int send(SOCKET socket, const void *buffer, size_t size) {
int r;
do {
r = send(socket, (const char *)buffer, size, 0);
if (r == 0 || r == SOCKET_ERROR) break;
buffer = (char *)buffer + r;
size -= r;
} while (size);
return r;
}
//get message from socket
static bool receive(SOCKET socket, MESSAGE &msg) {
int r = receive(socket, &msg, sizeof(MESSAGE_HEADER));
if (r == SOCKET_ERROR || r == 0) return false;
if (ntohs(msg.length) == 0) return true;
r = receive(socket, msg.buffer, ntohs(msg.length));
if (r == SOCKET_ERROR || r == 0) return false;
return true;
}
//send message
static bool send(SOCKET socket, const MESSAGE &msg) {
int r = send(socket, &msg, ntohs(msg.length) + sizeof(MESSAGE_HEADER));
if (r == SOCKET_ERROR || r == 0) return false;
return true;
}
「orientation」というメッセージを受信すると、「azimuth」の値がサーバーから送信された値と異なる場合があります。
データは常に同じであるべきではありませんか?TCP / IPは、データが破損しないように配信することを保証しませんか?数学コプロセッサーの例外がTCP/IPスタックに影響を与える可能性がありますか?最初に少数のバイト(4バイト)を受信し、次にメッセージ本文を受信するのは問題ですか?
編集:
問題は、エンディアンスワッピングルーチンにあります。次のコードは、特定のフロートのエンディアンを交換してから、再度交換してバイトを出力します。
#include <iostream>
using namespace std;
float ntohf(float f)
{
float r;
unsigned char *s = (unsigned char *)&f;
unsigned char *d = (unsigned char *)&r;
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
return r;
}
int main() {
unsigned long l = 3206974079;
float f1 = (float &)l;
float f2 = ntohf(ntohf(f1));
unsigned char *c1 = (unsigned char *)&f1;
unsigned char *c2 = (unsigned char *)&f2;
printf("%02X %02X %02X %02X\n", c1[0], c1[1], c1[2], c1[3]);
printf("%02X %02X %02X %02X\n", c2[0], c2[1], c2[2], c2[3]);
getchar();
return 0;
}
出力は次のとおりです。
7F 8A 26 BF 7F CA 26 BF
つまり、float割り当てはおそらく値を正規化し、元の値とは異なる値を生成します。
これに関するご意見をお待ちしております。
EDIT2:
返信ありがとうございます。問題は、スワップされたfloatが、「return」ステートメントを介して返されるときに、CPUの浮動小数点スタックにプッシュされることであるように思われます。次に、呼び出し元はスタックから値をポップし、値は丸められますが、それはスワップされた浮動小数点数であるため、丸めによって値が台無しになります。