如何在多线程服务器客户端程序中处理数据包?

时间:2011-05-09 15:14:48

标签: c windows multithreading winsock client-server

我目前有一个有效的客户端应用程序,但它是单线程的。

我的数据包看起来像这样:< 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;
 }

2 个答案:

答案 0 :(得分:1)

调试MT问题非常困难,特别是在一次删除时,如果你使用的是静态缓冲区,则不应该:

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

是:

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

为什么在任何情况下都使用静态缓冲区?

答案 1 :(得分:0)

我很想知道你是否在两个线程中都使用了相同的socked描述符来连接服务器。