首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Linux C Socket + MySQL:登錄驗證失敗後後續正確數據也判斷錯誤的根因排查

     編輯:程式設計疑難解答

典型代碼缺陷模式

// 全局/靜態變量存儲查詢結果或連接狀態
MYSQL_RES *result;
MYSQL_ROW row;
int login_ok = 0;

void handle_client(int sockfd) {
    char buf[1024];
    while (recv(sockfd, buf, sizeof(buf), 0) > 0) {
        // 解析用戶名密碼
        // ...
        // 執行查詢
        mysql_query(conn, sql);
        result = mysql_store_result(conn); // 未檢查返回值!
        row = mysql_fetch_row(result);     // 若上次查詢失敗,result 可能仍指向舊結果集!
        
        if (row && strcmp(row[0], "1") == 0) {
            login_ok = 1;
            send(sockfd, "OK", 2, 0);
        } else {
            login_ok = 0;
            send(sockfd, "FAIL", 4, 0);
        }
        // 忘記 mysql_free_result(result) → 內存洩漏、結果集殘留
    }
}

核心根因:資源未釋放導致狀態污染

錯誤操作後果
查詢失敗未檢查 mysql_query 返回值繼續執行 mysql_store_result 返回 NULL 或舊指針
未調用 mysql_free_result(result)結果集內存洩漏,下次查詢可能讀到殘留數據
全局 MYSQL_RES *result 被多線程/多客戶端共用線程不安全,數據交叉覆蓋
預處理語句 MYSQL_STMT 未 mysql_stmt_close參數綁定殘留,導致後續綁定錯誤

修正代碼模板

int verify_login(MYSQL *conn, const char *user, const char *pass) {
    // 1. 使用預處理語句防注入、提高性能
    const char *sql = "SELECT 1 FROM users WHERE username=? AND password=? LIMIT 1";
    MYSQL_STMT *stmt = mysql_stmt_init(conn);
    if (!stmt) return -1;
    
    if (mysql_stmt_prepare(stmt, sql, strlen(sql)) != 0) {
        mysql_stmt_close(stmt);
        return -1;
    }
    
    // 2. 綁定參數
    MYSQL_BIND bind[2];
    memset(bind, 0, sizeof(bind));
    bind[0].buffer_type = MYSQL_TYPE_STRING;
    bind[0].buffer = (char*)user;
    bind[0].buffer_length = strlen(user);
    bind[1].buffer_type = MYSQL_TYPE_STRING;
    bind[1].buffer = (char*)pass;
    bind[1].buffer_length = strlen(pass);
    mysql_stmt_bind_param(stmt, bind);
    
    // 3. 執行
    if (mysql_stmt_execute(stmt) != 0) {
        mysql_stmt_close(stmt);
        return -1;
    }
    
    // 4. 綁定結果
    MYSQL_BIND res_bind;
    memset(&res_bind, 0, sizeof(res_bind));
    int exists = 0;
    res_bind.buffer_type = MYSQL_TYPE_LONG;
    res_bind.buffer = &exists;
    mysql_stmt_bind_result(stmt, &res_bind);
    
    // 5. 獲取結果
    int ret = mysql_stmt_fetch(stmt); // 0=成功, 1=無數據, 其他=錯誤
    
    // 6. **關鍵:釋放資源**
    mysql_stmt_close(stmt);
    
    return (ret == 0 && exists) ? 1 : 0;
}

調試檢查清單

  1. 每次 mysql_query / mysql_stmt_execute **必檢返回值**。
  2. 每次 mysql_store_result / mysql_use_result **必配 mysql_free_result**。
  3. 連接對象 MYSQL *conn **不要跨線程共享**,每線程各自 mysql_init / mysql_real_connect。
  4. 啟用 MySQL 客戶端庫日志:mysql_options(conn, MYSQL_REPORT_DATA_TRUNCATION, ...) 或查看服務端 general_log。
  5. 借助 Valgrind / AddressSanitizer 檢測內存錯誤:gcc -fsanitize=address -g。

總結

「第一次對,第二次錯」經典癥狀指向 **MySQL 結果集/語句資源未正確釋放**,導致後續操作讀到髒數據或操作失效句柄。嚴格遵循「申請→使用→釋放」閉環,改用預處理語句,問題即刻消失。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved