我有一个从服务器读取大文件并经常挂在特定计算机上的应用程序。它已经在RHEL5.2下成功运行了很长时间。我们最近已升级到RHEL6.1,现在可以正常挂起。
我创建了一个重现问题的测试应用。它可以挂在100中的98次。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
int mFD = 0;
void open_socket()
{
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
if (getaddrinfo("localhost", "60000", &hints, &res) != 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
mFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (mFD == -1)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
if (connect(mFD, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
freeaddrinfo(res);
}
void read_message(int size, void* data)
{
int bytesLeft = size;
int numRd = 0;
while (bytesLeft != 0)
{
fprintf(stderr, "reading %d bytes\n", bytesLeft);
/* Replacing MSG_WAITALL with 0 works fine */
int num = recv(mFD, data, bytesLeft, MSG_WAITALL);
if (num == 0)
{
break;
}
else if (num < 0 && errno != EINTR)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
else if (num > 0)
{
numRd += num;
data += num;
bytesLeft -= num;
fprintf(stderr, "read %d bytes - remaining = %d\n", num, bytesLeft);
}
}
fprintf(stderr, "read total of %d bytes\n", numRd);
}
int main(int argc, char **argv)
{
open_socket();
uint32_t raw_len = atoi(argv[1]);
char raw[raw_len];
read_message(raw_len, raw);
return 0;
}
我的测试中有一些注意事项:
可在以下位置找到测试应用程序的源:
套接字测试源
从Loopback接口捕获的tcpdump可以在以下位置找到:
tcpdump捕获
我通过发出以下命令来重现该问题:
> gcc socket_test.c -o socket_test
> perl -e 'for (1..6000000){ print "a" }' | nc -l 60000
> ./socket_test 6000000
这将看到发送到测试应用程序的6000000字节,该应用程序尝试通过对recv()的一次调用来读取数据。
我很乐意听到关于我可能做错了什么的任何建议,或任何其他调试问题的方法。
MSG_WAITALL
应该
阻塞,直到收到所有数据。在recv的手册页中:
该标志请求操作块,直到满足完整请求为止。
但是,网络堆栈中的缓冲区可能不够大,无法容纳所有内容,这就是服务器上出现错误消息的原因。客户端网络堆栈根本无法容纳那么多数据。
解决方案是增加缓冲区大小(的SO_RCVBUF
选项setsockopt
),将消息拆分为较小的片段,或接收较小的块将其放入您自己的缓冲区中。最后是我的建议。
编辑: 我在您的代码中看到您已经按照我的建议进行操作(使用自己的缓冲读取较小的块),因此只需删除该MSG_WAITALL
标志即可。
哦,当recv
返回零时,意味着另一端已关闭连接,您也应该这样做。
有人知道这个问题吗?如何强制Windows XP也接收部分数据? 我将缓冲区大小(SO_RCVBUF)设置为1000字节。在Windows7上,这也反映到TCP窗口大小上--在XP上则不然。 这个问题的真正问题是,我不知道如何检查连接是否还活着。如何检查连接是否仍然有效?或者如何指定超时(从服务器接收的两个数据包之间的最大时间)?
我想广播消息本地到许多应用程序。对于这一点,我认为UDP套接字是最好的IPC,纠正我,如果我是Worwn。 并倾听: 问题是我必须像这样通过IP192.168.1.255,但在实际场景中可能没有eth0接口,只有环回。那我怎么才能做到这一点呢?
我有几个关于Java套接字编程的问题。 > 我有一个进程需要跨多个套接字连接发送一条消息。现在我有如下内容 这是原子能的吗?或者,在并发环境中,它们是否有可能在几乎相同的时间发送? 在并发环境中,套接字连接(设置为PrintWriter)的输出流是否已经互斥?比如说,两个线程想要同时写入PrintWriter。如果没有在PrintWriter上显式互斥,流的任何输出都会被弄乱吗?
我编写了一个基于Netty4的REST服务器。客户端处理程序如下所示。 netty提供的msg中的bytebuffer容量各不相同。当客户端消息大于缓冲区时,消息将被拆分。我发现每个片段都调用channelRead和ChannelReadComplete。我通常看到的是ByteBuf在512左右,message在600左右。对于前512个字节,我得到一个channelRead,然后是一个Chann
所以我写了这个服务器脚本,它应该接收一个用户名,然后继续其他一些代码。但是我收到了这个错误: "OSError:[WinError 10057]不允许发送或接收数据的请求,因为套接字未连接,并且(当使用sendto调用在数据报套接字上发送时)未提供地址" 我的理论是服务器和客户端没有同步,所以服务器认为它没有收到消息。如何改进我的代码,以便服务器实际接收消息?(我试过尝试块) 我的代码: sock
我是套接字编程和Netty框架的新手。我试图修改Echo服务器的例子,这样就不会一收到消息就从客户端发送消息,而是来自另一个线程的调用会触发客户端向服务器发送消息。 问题是,服务器不会收到消息,除非客户端从读取通道或消息接收或通道活动发送消息,这些消息是使用参数指定服务器的位置(通道处理程序上下文)。我无法设法找到一种方法来保存服务器通道并在以后重复发送消息。 这是我的客户处理程序代码; 当Cli