【发布时间】:2016-05-24 21:21:44
【问题描述】:
3 条不同的消息以不同的速率发送到同一个端口:
消息 大小(字节) 每发送一次 kbd>传输速度
高 232 10 ms 100Hz
中等 148 50Hz
低20 60 ms 16.6Hz
我只能每 ~ 6 毫秒处理一条消息。
单线程。阻止读取。
发生了一种奇怪的情况,我对此没有任何解释。
当我将接收缓冲区设置为 4,799 字节时,我的所有低速消息都会被丢弃。
我看到可能有一两个得到处理,然后什么都没有。
当我将接收缓冲区设置为4,800(或更高!)时,似乎所有低速消息都开始得到处理。我看到大约 16/17 秒。
这一直被观察到。发送数据包的应用程序总是在接收应用程序之前启动。接收应用程序在创建套接字之后和开始处理之前总是有很长的延迟。所以处理开始时缓冲区总是满的,而且每次发生测试时它都不是同一个开始缓冲区。这是因为套接字是在发送者已经发送消息之后创建的,因此接收者可能会在发送周期的中间开始监听。
为什么将接收缓冲区大小增加一个字节,会导致低速消息处理发生巨大变化?
随着其中一些消息得到处理,更多消息可能会被放入队列而不是被丢弃。
尽管如此,我希望4,799 字节缓冲区的行为方式与4,800 字节相同。
但这不是我观察到的。
我认为这个问题与低速消息与其他两条消息同时发送的事实有关。它总是在高速/中速消息之后接收。 (这已经通过wireshark得到证实)。
例如,假设缓冲区一开始是空的,很明显低速消息需要比其他消息更长的排队时间。
*每 6 毫秒 1 条消息大约是每 30 毫秒 5 条消息。
这仍然不能解释缓冲区大小。
我们正在运行 VxWorks,并使用他们的 sockLib,它是 Berkeley 套接字的一个实现。这是我们创建套接字的样子的 sn-p:
SOCKET_BUFFER_SIZE 是我要更改的内容。
struct sockaddr_in tSocketAddress; // Socket address
int nSocketAddressSize = sizeof(struct sockaddr_in); // Size of socket address structure
int nSocketOption = 0;
// Already created
if (*ptParameters->m_pnIDReference != 0)
return FALSE;
// Create UDP socket
if ((*ptParameters->m_pnIDReference = socket(AF_INET, SOCK_DGRAM, 0)) == ERROR)
{
// Error
CreateSocketMessage(ptParameters, "CreateSocket: Socket create failed with error.");
// Not successful
return FALSE;
}
// Valid local address
if (ptParameters->m_szLocalIPAddress != SOCKET_ADDRESS_NONE_STRING && ptParameters->m_usLocalPort != 0)
{
// Set up the local parameters/port
bzero((char*)&tSocketAddress, nSocketAddressSize);
tSocketAddress.sin_len = (u_char)nSocketAddressSize;
tSocketAddress.sin_family = AF_INET;
tSocketAddress.sin_port = htons(ptParameters->m_usLocalPort);
// Check for any address
if (strcmp(ptParameters->m_szLocalIPAddress, SOCKET_ADDRESS_ANY_STRING) == 0)
tSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
// Convert IP address for binding
if ((tSocketAddress.sin_addr.s_addr = inet_addr(ptParameters->m_szLocalIPAddress)) == ERROR)
{
// Error
CreateSocketMessage(ptParameters, "Unknown IP address.");
// Cleanup socket
close(*ptParameters->m_pnIDReference);
*ptParameters->m_pnIDReference = ERROR;
// Not successful
return FALSE;
}
}
// Bind the socket to the local address
if (bind(*ptParameters->m_pnIDReference, (struct sockaddr *)&tSocketAddress, nSocketAddressSize) == ERROR)
{
// Error
CreateSocketMessage(ptParameters, "Socket bind failed.");
// Cleanup socket
close(*ptParameters->m_pnIDReference);
*ptParameters->m_pnIDReference = ERROR;
// Not successful
return FALSE;
}
}
// Receive socket
if (ptParameters->m_eType == SOCKTYPE_RECEIVE || ptParameters->m_eType == SOCKTYPE_RECEIVE_AND_TRANSMIT)
{
// Set the receive buffer size
nSocketOption = SOCKET_BUFFER_SIZE;
if (setsockopt(*ptParameters->m_pnIDReference, SOL_SOCKET, SO_RCVBUF, (char *)&nSocketOption, sizeof(nSocketOption)) == ERROR)
{
// Error
CreateSocketMessage(ptParameters, "Socket buffer size set failed.");
// Cleanup socket
close(*ptParameters->m_pnIDReference);
*ptParameters->m_pnIDReference = ERROR;
// Not successful
return FALSE;
}
}
并且在无限循环中调用的套接字接收:
*缓冲区大小肯定足够大
int SocketReceive(int nSocketIndex, char *pBuffer, int nBufferLength)
{
int nBytesReceived = 0;
char szError[256];
// Invalid index or socket
if (nSocketIndex < 0 || nSocketIndex >= SOCKET_COUNT || g_pnSocketIDs[nSocketIndex] == 0)
{
sprintf(szError,"SocketReceive: Invalid socket (%d) or ID (%d)", nSocketIndex, g_pnSocketIDs[nSocketIndex]);
perror(szError);
return -1;
}
// Invalid buffer length
if (nBufferLength == 0)
{
perror("SocketReceive: zero buffer length");
return 0;
}
// Send data
nBytesReceived = recv(g_pnSocketIDs[nSocketIndex], pBuffer, nBufferLength, 0);
// Error in receiving
if (nBytesReceived == ERROR)
{
// Create error string
sprintf(szError, "SocketReceive: Data Receive Failure: <%d> ", errno);
// Set error message
perror(szError);
// Return error
return ERROR;
}
// Bytes received
return nBytesReceived;
}
任何关于为什么将缓冲区大小增加到 4,800 会导致成功且一致地读取低速消息的线索?
【问题讨论】:
-
尽管如此,4,799 字节足以预期与 4,800 字节相同的行为。 为什么要这么说? 9,999 字节不足以容纳 10,000 字节的文件。 任何关于为什么将缓冲区大小增加到 4,800 会导致成功且一致地读取低速消息的任何线索? 这不是线索,但我怀疑这是因为 4,800 butes 足够大,而 4,799 则不够大。您可以使用 4,800 字节缓冲区,也可以花费大量时间和精力让 Wind River 告诉您为什么在您的场景中需要使用 4,800 字节缓冲区 - 然后您将使用 4,800 字节的缓冲区。
-
在setsockopt on vxworks 页面上声明“UDP 保留 41600 字节,足够空间容纳多达 40 个传入数据报(每个 1 KB)。”。您使用的是 1/8.6,因此有足够的空间容纳大约 4 个数据包。我想这一切都取决于 NIC 和驱动程序的实现——它们通常使用“描述符环”,具有许多固定大小的数据包缓冲区来保存大多数数据包。如果这些都是 1kb(并且较大的数据包被分成两个或更多缓冲区),则接收一堆较小的数据包将耗尽数据包缓冲区。
-
对于实际的套接字接收缓冲区来说,4799 和 4800 都太小了。您至少应该考虑 32-64k。
-
可能是交换机问题——交换机中的某些东西被配置为避免泛洪
标签: c sockets networking udp vxworks