我不确定这是否是我遇到的已知问题,但我找不到能给我任何有用结果的好搜索字符串。 无论如何,这是基本的纲要:
我们有一个相对简单的应用程序,它从源(数据库或文件)获取数据,并在新数据进入时通过TCP将数据流传输到连接的客户端。它的客户端数量相对较少;我会说每台服务器最多10个客户端,所以我们有以下粗略设计:
客户端:连接到服务器,设置为读取(超时设置为高于服务器心跳消息频率)。它会阻止阅读。
server:一个接受连接的侦听线程,然后生成一个写入线程以从数据源读取并写入客户端。编写器线程也是分离的(使用boost :: thread所以只需调用.detach()函数)。它会无限制地阻止写入,但在写入之前会检查错误是否有错误。我们使用单个perl脚本启动服务器,并为每个服务器进程调用“fork”。
问题: 在看似随机的时间,客户端将关闭“连接已终止(SUCCESFUL)”,表示远程服务器故意关闭套接字。但是,当发生这种情况时,SERVER应用程序也会关闭,没有任何错误或任何错误。它只是崩溃了。
现在,为了解决这个问题,我们有多个服务器应用程序实例由运行不同文件和不同端口的启动脚本启动。当其中一台服务器崩溃时,所有服务器都崩溃了。
服务器和客户端都使用内部创建的相同“连接”库。它主要是C套接字调用的C ++包装器。
这里是Connection libary中写入和读取函数的一些粗略代码:
int connectionTimeout_read = 60 * 60 * 1000;
int Socket::readUntil(char* buf, int amount) const
{
int readyFds = epoll_wait(epfd,epEvents,1,connectionTimeout_read);
if(readyFds < 0)
{
status = convertFlagToStatus(errno);
return 0;
}
if(readyFds == 0)
{
status = CONNECTION_TIMEOUT;
return 0;
}
int fd = epEvents[0].data.fd;
if( fd != socket)
{
status = CONNECTION_INCORRECT_SOCKET;
return 0;
}
int rec = recv(fd,buf,amount,MSG_WAITALL);
if(rec == 0)
status = CONNECTION_CLOSED;
else if(rec < 0)
status = convertFlagToStatus(errno);
else
status = CONNECTION_NORMAL;
lastReadBytes = rec;
return rec;
}
int Socket::write(const void* buf, int size) const
{
int readyFds = epoll_wait(epfd,epEvents,1,-1);
if(readyFds < 0)
{
status = convertFlagToStatus(errno);
return 0;
}
if(readyFds == 0)
{
status = CONNECTION_TERMINATED;
return 0;
}
int fd = epEvents[0].data.fd;
if(fd != socket)
{
status = CONNECTION_INCORRECT_SOCKET;
return 0;
}
if(epEvents[0].events != EPOLLOUT)
{
status = CONNECTION_CLOSED;
return 0;
}
int bytesWrote = ::send(socket, buf, size,0);
if(bytesWrote < 0)
status = convertFlagToStatus(errno);
lastWriteBytes = bytesWrote;
return bytesWrote;
}
解决这个神秘错误的任何帮助都会很棒!至少,我希望它不会崩溃服务器,即使客户端崩溃(这对我来说真的很奇怪,因为没有双向通信)。
另外,作为参考,这是服务器监听代码:
while(server.getStatus() == connection::CONNECTION_NORMAL)
{
connection::Socket s = server.listen();
if(s.getStatus() != connection::CONNECTION_NORMAL)
{
fprintf(stdout,"failed to accept a socket. error: %s\n",connection::getStatusString(s.getStatus()));
}
DATASOURCE* dataSource;
dataSource = open_datasource(XXXX); /* edited */ if(dataSource == NULL)
{
fprintf(stdout,"FATAL ERROR. DATASOURCE NOT FOUND\n");
return;
}
boost::thread fileSender(Sender(s,dataSource));
fileSender.detach();
}
......这里也是产生线程的衍生儿童:
::signal(SIGPIPE,SIG_IGN);
//const int headerNeeds = 29;
const int BUFFERSIZE = 2000;
char buf[BUFFERSIZE];
bool running = true;
while(running)
{
memset(buf,'\0',BUFFERSIZE*sizeof(char));
unsigned int readBytes = 0;
while((readBytes = read_datasource(buf,sizeof(unsigned char),BUFFERSIZE,dataSource)) == 0)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
}
socket.write(buf,readBytes);
if(socket.getStatus() != connection::CONNECTION_NORMAL)
running = false;
}
fprintf(stdout,"socket error: %s\n",connection::getStatusString(socket.getStatus()));
socket.close();
fprintf(stdout,"sender exiting...\n");
欢迎任何见解!提前谢谢。
答案 0 :(得分:2)
你可能让所有东西倒退......当服务器崩溃时,操作系统将关闭所有套接字。因此服务器崩溃首先发生并导致客户端获取断开连接消息(实际上是TCP段中的FIN标志),崩溃不是套接字关闭的结果。
由于您有多个服务器进程同时崩溃,我会查看它们共享的资源,以及所有服务器同时尝试执行的任何计划任务。
编辑:你没有一个客户端连接到多个服务器,对吗?请注意,TCP连接始终是双向的,因此如果客户端断开连接,服务器进程会收到反馈。一些互联网提供商甚至已经发现在连接上生成RST数据包,这些连接未通过某些可疑流量测试。
写一个信号处理程序。确保它仅使用原始I / O函数来记录问题(打开,写入,关闭,而不是fwrite
,而不是printf
)。
检查返回值。检查套接字上write
的负返回值,但检查所有返回值。
答案 1 :(得分:0)
感谢所有意见和建议。 在查看代码并添加Ben建议的信号处理之后,应用程序本身更加稳定。感谢您的所有意见。
然而,最初的问题是由于一个流氓脚本,其中一个管理员以root身份运行,会随机杀死服务器端计算机上的某些进程(我不会进入它尝试做的事情)现实;可以肯定地说它是错误的)。 获得的经验:检查环境。
谢谢大家的建议。