2011-05-09 45 views
1

我目前有一個客戶端應用程序,但它是單線程的。如何處理多線程服務器客戶端程序中的數據包?

我的數據包是這樣的:< len_of_data> | <數據>」

「|」 被用作我的數據的分離

< len_of_data>總是長跟着4位

<數據>的樣子:|。<事務ID> | <命令> | < buflen> | < BUF> | <校驗> |

我的代碼來創建數據包是:

_snprintf_s(data_buffer, WS_MAX_DATA_PACKET_SIZE, 
       WS_MAX_DATA_PACKET_SIZE - 1, 
       "%s%d%s%d%s%d%s%s%s%d%s", 
       WS_PACKET_SEP, pkt->transaction_id, 
       WS_PACKET_SEP, pkt->command, 
       WS_PACKET_SEP, pkt->bufsize, 
       WS_PACKET_SEP, pkt->buf, 
       WS_PACKET_SEP, pkt->checksum, WS_PACKET_SEP); 

buf_len = strlen(data_buffer); 

_snprintf_s(send_buffer, WS_MAX_DATA_PACKET_SIZE, 
      WS_MAX_DATA_PACKET_SIZE - 1, "%04d%s%s", 
      buf_len, WS_PACKET_SEP, data_buffer); 

buf_len = strlen(send_buffer); 
// Send buffer 
bytes_sent = send(ConnectSocket, send_buffer, buf_len, 0); 

客戶端線程向服務器發送命令,然後調用GetIncomingPackets()函數。在GetIncomingPackets()中,我調用recv()來獲得5個字節,這應該是其餘數據包的len,我解析這5個字節並驗證它們是否符合我的預期格式。然後我將前4個字節轉換爲一個整數x。然後我再次調用recv()來獲得更多的x字節,然後將它們解析到我的數據包結構中。

當我添加另一個線程來做同樣的事情(發送和接收命令)時,會發生問題。 我開始我的應用程序並激發2個線程併發送它們發送不同的命令並等待響應。當線程調用GetIncomingPackets()時,我回來的數據是無效的。我期望的前5個字節有時會丟失,而我只會得到以下5個字節,因此我無法獲取我的len_of_data>數據包。

我甚至在我的GetIncomingPackets()中的2個recv()調用之間添加了一個臨界區塊,以便在獲取完整數據包時不會相互中斷。 沒有錯誤檢查一些額外的代碼,此功能如何看起來像

#define WS_SIZE_OF_LEN_PACKET 5 
bool GetIncomingPackets(SOCKET sd, dev_sim_packet_t *pkt) 
{ 
    char len_str_buf[WS_SIZE_OF_LEN_PACKET + 1] = {0};  // + 1 for NULL char 
    char data_buf[WS_MAX_DATA_PACKET_SIZE + 1] = {0}; 
    int ret = 0; 
    int data_len = 0; 

    EnterCriticalSection(&recv_critical_section); 
    nReadBytes = WS_RecvAll(sd, len_str_buf, WS_SIZE_OF_LEN_PACKET); 
    ret = WS_VerifyLenPacket(len_str_buf); 
    // Convert data packet lenght string received to int 
    data_len = WS_ConvertNumberFromString(len_str_buf, WS_SIZE_OF_LEN_PACKET); 
    // Get data from packet 
    nReadBytes = WS_RecvAll(sd, data_buf, data_len); 
    LeaveCriticalSection(&recv_critical_section ); 
    ret = ParseMessager(data_buf, data_len, pkt); 
} 

我的問題是,這可能是造成這個問題,我怎麼能解決這個問題?或者有更好的方法來做我想做的事情。我試圖讓它成爲多線程的原因是因爲我的應用程序將與其他兩個源進行通信,並且我想要一個線程來處理來自任一來源的每個請求。

在此先感謝您,如果我沒有解釋清楚,請隨時提出任何問題。

下面是WS_RecvAll()的代碼。該緩衝區),這樣在GetIncomingPackets(聲明靜態緩衝區:

char data_buf[WS_MAX_DATA_PACKET_SIZE + 1] = {0}; // + 1 for NULL char 


int WS_RecvAll(SOCKET socket_handle, char* buffer, int size) 
{ 
    int ret = 0; 
    int read = 0; 
    int i = 0; 
    char err_buf[100] = {0}; 
    while(size) 
    { 
     ret = recv(socket_handle, &buffer[read], size, 0); 
     if (ret == SOCKET_ERROR) 
     { 
      printf("***ERROR***: recv failed, error = %d\n", WSAGetLastError()); 
      return WS_ERROR_RECV_FAILED; 
     } 
     if (ret == 0) { 
      break; 
     } 
     read += ret; 
     size -= ret; 
    } 
    return read; 
} 
+0

客戶端和服務器之間的協議是什麼? (即TCP還是UDP?) – Nick 2011-05-09 15:35:59

+0

它是TCP ...... – emge 2011-05-09 15:46:45

+0

發佈WS_RecvAll的代碼。 – 2011-05-09 15:49:08

回答

1

這是調試MT問題非常困難,尤其是在一個刪除,但如果您使用的不安定緩衝,不應該:

​​

是:

ret = ParseMessager(data_buf, data_len, pkt); 
LeaveCriticalSection(&recv_critical_section ); 

爲什麼在任何情況下使用靜態緩衝區?

+0

好的,它沒有聲明爲靜態的。我的意思是,我不使用malloc分配它。我使用了一個大小不變的緩衝區來接收我的數據,因爲我總是知道我可以接收的最大數據是什麼。那有意義嗎?或者你認爲我應該每次都動態地分配緩衝區? – emge 2011-05-09 17:45:27

+0

@emge這是有道理的。不要將動態分配添加到您的困境中。但是,我仍然會嘗試移動LeaveCriticalSection調用。 – 2011-05-09 17:50:34

+0

@emge實際上,我只是重新讀取你的代碼,除非我錯過了你正在靜態分配緩衝區的東西 - 即不在函數中。 – 2011-05-09 18:00:03

0

我很好奇,知道你是否在兩個線程中使用了相同的socked描述符來連接到服務器。

+0

是的我對這兩個線程都使用了相同的SOCKET。 – emge 2011-05-09 20:33:12

相關問題