如何在多线程服务器客户端程序中处理数据包?
我目前有一个可以运行的客户端应用程序,但它是单线程的。
我的数据包看起来像这样:< len_of_data>|< data>"
“|”用作我的数据的分隔符。
看起来像:|< transaction id>|< command>|< buflen>|
我创建数据包的代码是:
_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() 函数中,我调用 recv() 来获取 5 个字节,这应该是数据包其余部分的 len,我解析这 5 个字节并验证它们是否符合我的预期格式,然后转换第一个字节。 4 字节为整数 x。然后我再次调用 recv() 来获取更多 x 字节,然后将它们解析到我的数据包结构中,
当我添加另一个线程执行相同的操作(发送和接收命令)时,就会出现问题。 我启动我的应用程序并触发 2 个线程,然后让它们发送不同的命令并等待响应。当线程调用 GetIncomingPackets() 时,我返回的数据无效。我期望的前 5 个字节有时会丢失,而我只得到以下 5 个字节,因此我无法得到我的 <数据长度>包。
我什至在 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);
}
我的问题是,什么可能导致此问题,以及如何修复它?或者有更好的方法来做我想做的事情。我尝试使其成为多线程的原因是因为我的应用程序将与其他 2 个源进行通信,并且我希望有一个线程来处理来自任一源的每个请求。
提前致谢,如果我没有解释清楚,请随时提出任何问题。
这是 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;
}
I currently have a client app that works but it is single threaded.
my packets look like this: < len_of_data>|< data>"
"|" is used as a separator for my data.
< len_of_data> is always 4 digits long followed.
< data> looks like: |< transaction id>|< command>|< buflen>|< buf>|< checksum>|
my code to create the packets is:
_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);
The client thread sends a command to the server, then calls a GetIncomingPackets() function. In GetIncomingPackets(), I call recv() to get 5 bytes, this should be the len of the rest of packet, I parse these 5 bytes and verify that they match my expected format. Then I convert the first 4 bytes to an integer, x. Then I call recv() again to get x bytes more and then parse those out into my packet structure.
The problem happens when I add another thread to do the same thing (send and receive commands).
I start my app and fire 2 threads and send them to send different commands and wait for responses. When the threads call GetIncomingPackets(), the data I am getting back is invalid. The first 5 bytes I am expecting are missing sometimes, and I just get the following 5 bytes, therefore I am unable to get my < len_of_data > packet.
I even added a critical section block between the 2 recv() calls in my GetIncomingPackets() so the treads dont interrupt each other while getting a full packet.
Without some extra code for error checking, this how the function looks like
#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);
}
My question is, what could be causing this problem, and how could I fix it? Or is there better ways to do what i am trying to do. The reason that I'm trying to make it multi-threaded is because my app will communicate with 2 other sources, and I want to have a thread to handle each request that comes in from either source.
thanks in advance and feel free to ask any questions if I didn't explain something well.
Here's the code for WS_RecvAll(). The buffer is a static buffer declared in GetIncomingPackets() like this:
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
调试 MT 问题非常困难,尤其是在一次删除时,但如果您使用静态缓冲区,则不应:
是:
为什么在任何情况下都使用静态缓冲区?
It's very difficult to debug MT problems, particularly at one remove, but if you are using astatic buffer, should not:
be:
And why use a static buffer in any case?
我很想知道您是否在两个线程中使用相同的 socked 描述符来连接到服务器。
Im curious to know whether you have used the same socked descriptor in both the threads to connect to the server.