【发布时间】:2011-05-09 15:14:48
【问题描述】:
我目前有一个可以运行的客户端应用程序,但它是单线程的。
我的数据包如下所示:
“|”用作我的数据的分隔符。
看起来像:|
我创建数据包的代码是:
_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 个字节,因此我无法获得我的
我什至在我的 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;
}
【问题讨论】:
-
客户端和服务器之间使用的协议是什么? (即 TCP 还是 UDP?)
-
发布 WS_RecvAll 的代码。
-
WS_RecvAll 如何分配data_buf?
-
我现在发布了代码 WS_RecvAll 作为问题的一部分。
-
在上面的 _snprintf_s 语句中,您正在使用 %s 格式化程序格式化消息。但是如果 pkt->buf 是二进制数据,那么任何超过第一个 nul 字节的数据都会被截断。
标签: c windows multithreading winsock client-server