Cでの文字列の比較-strcmp
-
06-07-2019 - |
質問
Cで文字列を比較するのに問題があります(これはかなり初めてです)。このサーバーアプリケーションに、クライアントからのデータの受信を待機しているソケットがあります。プログラムのこの特定の部分では、クライアントから受信したデータに基づいてMySQLクエリを実行できるようにしたいと考えています。受信したデータの値が「newuser」のときを知りたいのですが。簡単な登録手順を開始します。 strcmpは、値が等しくなければならないため、0を取得するはずであると考えている正の1値を返しています。
ソースコード:
//setup socket
//loop and select structure to handle multiple connections
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
char check[] = "newuser";
char fromUser[sizeof check];
strncpy(fromUser,buf, sizeof check);
printf("length of fromUser: %d\n", sizeof fromUser);
printf("length of check: %d\n", sizeof check);
printf("message from user: %s\n", fromUser);
printf("check = %s \n", check);
int diff = strcmp(fromUser, check);
printf("compare fromUser to check: %d\n", diff);
if ( strcmp(fromUser, check) == 0) {
printf("aha! new user");
}
出力:
length of fromUser: 8
length of check: 8
newuser from user: newuser
check = newuser
compare fromUser to check:
着信バッファを正しくまたは誤ってバッファをコピーしていないと感じています。
解決
strncpy
は最大でコピーします-この場合- sizeof
はバイトをチェックします。 NULバイトがその範囲にない場合、コピーされません。おそらく「newuser」という単語が表示されるでしょう。 「newuser blah blah」のような長い文の一部として。あなたはそれを自分で置く必要があります
strncpy(fromUser, buf, sizeof check);
fromUser[sizeof check - 1] = '\0';
または strlcpy
を使用します(使用可能な場合)。
他のヒント
質問で指定したサンプルコードを以下に示します(デバッグコードを削除):
//setup socket
//loop and select structure to handle multiple connections
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
[... exception handling here ...]
} else {
char check[] = "newuser";
char fromUser[sizeof check];
strncpy(fromUser,buf, sizeof check);
if ( strcmp(fromUser, check) == 0) {
printf("aha! new user");
}
このコードは間違っています。受信したよりも多くのバイトをbuf []からコピーする可能性があります。これにより、ガベージと比較することになります(偶然&quot; newuser&quot;文字列と一致する場合があります)。そして、他の人が言ったように、文字列の1つをNULで終了しないため、2番目のバグがあります。
この場合、memcmp()を使用します。これはstrcmp()に似ていますが、NULで終了する文字列を想定するのではなく、長さのパラメーターを取ります。
//setup socket
//loop and select structure to handle multiple connections
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
[... exception handling here ...]
} else {
static const char check[] = "newuser";
const size_t check_len = sizeof(check) - 1; // exclude the NUL terminator
if (nbytes >= check_len && memcmp(buf, check, check_len) == 0) {
printf("aha! new user");
}
PS直接関係ありませんが、 recv()は、 -1
と errno == EINTR
。これはエラー状態ではなく、もう一度試す必要があります。通常、これは非常にまれにしか発生しないため、信号を使用する他のコードと統合し、突然コードがランダムに失敗するまで、確認せずに逃げることができます。
select()
ベースのアプリでは、ソケットを非ブロッキングに設定し、 errno == EAGAIN
を確認してから、その場合は select()
を使用します。これは、TCP / IPスタックが破損したパケットを受信した場合に発生する可能性があります。パケットがあると考えられるため、 select()
は読み取り可能と表示します。 stackはチェックサムの計算を行い、データを破棄する必要があることを認識します。次に、ブロック(不良)するか、非ブロックに設定されている場合、 errno == EAGAIN
で -1
を返します。
ここでの問題(ここでの問題の1つ)は、fromUser(作成方法による)がnullで終了しないことだと思います。
fromUserの最後に「\ 0」文字がありません:
...
strncpy(fromUser,buf, sizeof check);
fromUser[strlen(check)] = '\0';
2つの変更が必要:
char fromUser[sizeof check] = {'\0'}; //Make all null characters
strncpy(fromUser,buf, sizeof check -1); //Last character is for null character.
このコードはオフのようです:
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
{
// your stuff
}
else {
const char *pCheck = "newuser";
char *fromUser = new char[nbytes];
strncpy(fromUser, buff, nbytes);
fromUser[nbytes] = '\0';
if(strcmp(fromUser,check)==0)
// blah
delete [] fromUser;
}
置換:
char check[] = "newuser\0";