功能 setsockopt 似乎不起作用
Function setsockopt seems not working
我正在使用 WinSock2,我想一次收到我所有的包(它们是 XML 文件)。
现在我正在做如下,因为 setsockopt 似乎忽略了我的 "order" 来设置接收缓冲区的大小,但它总是 returns 从执行中确定:
#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read = 0;
int iBufSize = IP_BUF_SZ;
//...
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int)) == SOCKET_ERROR)
MessageBox("Unable to increase"); //never reachs here
do
{
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(sockfd, buf, IP_BUF_SZ, 0);
if (chrs_read > 0)
sBuffer += std::string(buf);
FREE(buf);
buf = NULL;
}
while (chrs_read > 0);
但我想做类似的事情(一次完成):
setsockopt(IP_STATUS[CHAN_EPIC].sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int);
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(IP_STATUS[CHAN_EPIC].sockfd, buf, IP_BUF_SZ, 0);
sBuffer += std::string(buf);
备注:我的文件小于 IP_BUF_SZ 大小。
提前谢谢你。
您可以将缓冲区设置得与系统允许的一样大,但是您无法立即从 TCP 接收所有数据。您必须循环直到收到所有您期望的数据。
你的问题是基于错误的假设。
setsockopt(SO_RCVBUF)
不保证您可以在单个 recv()
调用中收到完整消息。它甚至不能保证您将获得您请求的缓冲区大小。您必须使用 getsockopt(SO_RCVBUF)
来检索实际缓冲区大小。
SO_RCVBUF
只是控制在发送方开始阻塞等待您读取字节之前,套接字可以在其内部缓冲区中保存多少字节。但是 recv()
不保证它会 return 您请求的所有字节(除非您使用 MSG_WAITALL
标志调用它,在这种情况下我不建议这样做,因为您不事先知道消息长度)。如果您要求 recv()
读取 X 字节,而当前只有 Y 字节可用,其中 Y < X,recv()
将 return Y 字节而不是等待 return X 字节.唯一的保证是 recv()
将 return 至少 1 个字节并且 不会超过 X 个字节。因此,无论您将内部缓冲区大小设置为多少,您仍然需要一个读取循环。
SO_RCVBUF
只是用于优化网络的建议选项 I/O。不要真的依赖它来实现代码的阅读逻辑。
也就是说,您显示的代码还有其他问题。
您正在为循环的每次迭代分配和释放接收缓冲区。不要那样做。分配一次,然后循环读取,然后在完成后释放缓冲区。
没有必要 over-allocate 您的接收缓冲区超出您实际从 recv()
请求的内容。读取时也无需将缓冲区清零。这些步骤只是浪费了开销。
在将接收缓冲区附加到 std::string
时,您也没有考虑 recv()
的 return 值。您正在使用期望缓冲区为 null-terminated 的 std::string
构造函数。您依靠缓冲区清零来提供 null-terminator,但如果数据包含其自身的任何嵌入 nul 字符(取决于 XML 的编码),那么这将截断您附加到std::string
。 recv()
returns 它读取了多少字节。您需要向 std::string
追加恰好那么多字节。
试试这样的东西:
#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read;
//...
int iBufSize = IP_BUF_SZ;
int iBufVarSize = sizeof(iBufSize);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, iBufVarSize) == SOCKET_ERROR)
MessageBox("Unable to set buffer size");
else if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, &iBufVarSize) == SOCKET_ERROR)
{
MessageBox("Unable to get buffer size");
iBufSize = IP_BUF_SZ;
}
char* buf = (char*) malloc(iBufSize);
if (!buf)
MessageBox("Unable to allocate buffer");
else
{
do
{
chrs_read = recv(sockfd, buf, iBufSize, 0);
if (chrs_read <= 0)
{
if (chrs_read == SOCKET_ERROR)
MessageBox("Unable to read message");
break;
}
sBuffer.append(buf, chrs_read);
}
while (true);
free(buf);
buf = NULL;
}
注意这个逻辑一直在读,直到socket断开或者读时遇到错误。如果每个套接字连接只有 1 条消息,那也没关系。但是,如果您打算在单个连接上发送多个 XML 消息,那将不再有效。您需要在消息之间放置一个分隔符,以便您知道一条消息的结束位置和下一条消息的开始位置。这可以是指定消息长度的前导 header,也可以是消息末尾的唯一终止符序列。
我正在使用 WinSock2,我想一次收到我所有的包(它们是 XML 文件)。 现在我正在做如下,因为 setsockopt 似乎忽略了我的 "order" 来设置接收缓冲区的大小,但它总是 returns 从执行中确定:
#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read = 0;
int iBufSize = IP_BUF_SZ;
//...
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int)) == SOCKET_ERROR)
MessageBox("Unable to increase"); //never reachs here
do
{
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(sockfd, buf, IP_BUF_SZ, 0);
if (chrs_read > 0)
sBuffer += std::string(buf);
FREE(buf);
buf = NULL;
}
while (chrs_read > 0);
但我想做类似的事情(一次完成):
setsockopt(IP_STATUS[CHAN_EPIC].sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, sizeof(int);
char* buf = (char*)MALLOCZ(IP_BUF_SZ + 1000);
chrs_read = recv(IP_STATUS[CHAN_EPIC].sockfd, buf, IP_BUF_SZ, 0);
sBuffer += std::string(buf);
备注:我的文件小于 IP_BUF_SZ 大小。 提前谢谢你。
您可以将缓冲区设置得与系统允许的一样大,但是您无法立即从 TCP 接收所有数据。您必须循环直到收到所有您期望的数据。
你的问题是基于错误的假设。
setsockopt(SO_RCVBUF)
不保证您可以在单个 recv()
调用中收到完整消息。它甚至不能保证您将获得您请求的缓冲区大小。您必须使用 getsockopt(SO_RCVBUF)
来检索实际缓冲区大小。
SO_RCVBUF
只是控制在发送方开始阻塞等待您读取字节之前,套接字可以在其内部缓冲区中保存多少字节。但是 recv()
不保证它会 return 您请求的所有字节(除非您使用 MSG_WAITALL
标志调用它,在这种情况下我不建议这样做,因为您不事先知道消息长度)。如果您要求 recv()
读取 X 字节,而当前只有 Y 字节可用,其中 Y < X,recv()
将 return Y 字节而不是等待 return X 字节.唯一的保证是 recv()
将 return 至少 1 个字节并且 不会超过 X 个字节。因此,无论您将内部缓冲区大小设置为多少,您仍然需要一个读取循环。
SO_RCVBUF
只是用于优化网络的建议选项 I/O。不要真的依赖它来实现代码的阅读逻辑。
也就是说,您显示的代码还有其他问题。
您正在为循环的每次迭代分配和释放接收缓冲区。不要那样做。分配一次,然后循环读取,然后在完成后释放缓冲区。
没有必要 over-allocate 您的接收缓冲区超出您实际从 recv()
请求的内容。读取时也无需将缓冲区清零。这些步骤只是浪费了开销。
在将接收缓冲区附加到 std::string
时,您也没有考虑 recv()
的 return 值。您正在使用期望缓冲区为 null-terminated 的 std::string
构造函数。您依靠缓冲区清零来提供 null-terminator,但如果数据包含其自身的任何嵌入 nul 字符(取决于 XML 的编码),那么这将截断您附加到std::string
。 recv()
returns 它读取了多少字节。您需要向 std::string
追加恰好那么多字节。
试试这样的东西:
#define IP_BUF_SZ 2000000
//...
std::string sBuffer;
long chrs_read;
//...
int iBufSize = IP_BUF_SZ;
int iBufVarSize = sizeof(iBufSize);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, iBufVarSize) == SOCKET_ERROR)
MessageBox("Unable to set buffer size");
else if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&iBufSize, &iBufVarSize) == SOCKET_ERROR)
{
MessageBox("Unable to get buffer size");
iBufSize = IP_BUF_SZ;
}
char* buf = (char*) malloc(iBufSize);
if (!buf)
MessageBox("Unable to allocate buffer");
else
{
do
{
chrs_read = recv(sockfd, buf, iBufSize, 0);
if (chrs_read <= 0)
{
if (chrs_read == SOCKET_ERROR)
MessageBox("Unable to read message");
break;
}
sBuffer.append(buf, chrs_read);
}
while (true);
free(buf);
buf = NULL;
}
注意这个逻辑一直在读,直到socket断开或者读时遇到错误。如果每个套接字连接只有 1 条消息,那也没关系。但是,如果您打算在单个连接上发送多个 XML 消息,那将不再有效。您需要在消息之间放置一个分隔符,以便您知道一条消息的结束位置和下一条消息的开始位置。这可以是指定消息长度的前导 header,也可以是消息末尾的唯一终止符序列。