【发布时间】:2016-09-16 12:12:47
【问题描述】:
我已经尝试解决这个问题一个多月了。我无处可去。 我有一台服务器可以监听许多多播频道(100ish)。每个套接字都是它自己的线程。然后我有一个客户端侦听器(单线程),它处理同一服务器内的所有传入连接、断开连接和客户端消息传递。这个想法是客户端进来,连接,从多播通道请求数据,然后我将数据发送回客户端。客户端保持连接,我将 UDP 数据转发回客户端。客户端可以请求 UDP 或 TCP 具有用于数据中继的协议。在某一时刻,这在几周内运行良好。我们做了一些代码和内核更改,现在我们无法弄清楚出了什么问题。
服务器将运行数小时,并全天连接数百个客户端。但是在某些时候,服务器会随机停止。停止,我的意思是:所有 UDP 套接字停止接收/处理数据(tcpdump 显示数据仍在进入盒子),client_listener 线程停止接收客户端数据包。但!!!主 client_listener 套接字仍然可以在主套接字上接收新连接和新断开连接。在新连接上,主套接字能够将“连接已建立”数据包发送回客户端,但是当客户端响应时,选择永远不会返回。
如果有人愿意,我可以发布代码。如果有人有任何建议在哪里看,或者这听起来像什么。请告诉我。
如果您有任何问题,请提出。
谢谢。
我想分享我的 TCP 服务器代码: 这是一个单线程。几个小时都可以正常工作,然后我只会收到“新连接”和“断开连接”。没有客户数据包进来。
int opt = 1;
int addrlen;
int sd;
int max_sd;
int valread;
int activity;
int new_socket;
char buffer[MAX_BUFFER_SIZE];
int client_socket[m_max_clients];
struct sockaddr_in address;
fd_set readfds;
for(int i = 0; i<m_max_clients; i++)
{
client_socket[i]=0;
}
if((m_master_socket = socket(AF_INET,SOCK_STREAM,0))==0)
LOG(FATAL)<<"Unable to create master socket";
if(setsockopt(m_master_socket,SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(opt))<0)
LOG(FATAL)<<"Unable to set master socket";
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(m_listenPort);
if(bind(m_master_socket,(struct sockaddr*)& address, sizeof(address))!=0)
LOG(FATAL)<<"Unable to bind master socket";
if(listen(m_master_socket,SOMAXCONN)!=0)
LOG(FATAL)<<"listen() failed with err";
addrlen = sizeof(address);
LOG(INFO)<<"Waiting for connections......";
while(true)
{
FD_ZERO(&readfds);
FD_SET(m_master_socket, &readfds);
max_sd = m_master_socket;
for(int i = 0; i<m_max_clients; i++)
{
sd = client_socket[i];
if(sd > 0)
FD_SET(sd, &readfds);
if(sd>max_sd)
max_sd = sd;
}
activity = select(max_sd+1,&readfds,NULL,NULL,NULL);
if((activity<0)&&(errno!=EINTR))
{
// int err = errno;
// LOG(ERROR)<<"SELECT ERROR:"<<activity<<" "<<err;
continue;
}
if(FD_ISSET(m_master_socket, &readfds))
{
if((new_socket = accept(m_master_socket,(struct sockaddr*)&address, (socklen_t*)&addrlen))<0)
LOG(FATAL)<<"ERROR:ACCEPT FAILED!";
LOG(INFO)<<"New Connection, socket fd is (" << new_socket << ") client_addr:" << inet_ntoa(address.sin_addr) << " Port:" << ntohs(address.sin_port);
for(int i =0;i<m_max_clients;i++)
{
if(client_socket[i]==0)
{
//try to set the socket to non blocking, tcp nagle and keep alive
if ( !SetSocketBlockingEnabled(new_socket, false) )
LOG(INFO)<<"UNABLE TO SET NON-BLOCK: ("<<new_socket<<")" ;
if ( !SetSocketNoDelay(new_socket,false) )
LOG(INFO)<<"UNABLE TO SET DELAY: ("<<new_socket<<")" ;
// if ( !SetSocketKeepAlive(new_socket,true) )
// LOG(INFO)<<"UNABLE TO SET KeepAlive: ("<<new_socket<<")" ;
ClientConnection* con = new ClientConnection(m_mocSrv, m_udpPortGenerator, inet_ntoa(address.sin_addr), ntohs(address.sin_port), new_socket);
if(con->login())
{
client_socket[i] = new_socket;
m_clientConnectionSocketMap[new_socket] = con;
LOG(INFO)<<"Client Connection Logon Complete";
}
else
delete con;
break;
}
}//for
}
else
{
try{
for(int i = 0; i<m_max_clients; i++)
{
sd = client_socket[i];
if(FD_ISSET(sd,&readfds))
{
if ( (valread = recv(sd, buffer, sizeof(buffer),MSG_DONTWAIT|MSG_NOSIGNAL)) <= 0 )
{
//remove from the fd listening set
LOG(INFO)<<"RESET CLIENT_SOCKET:("<<sd<<")";
client_socket[i]=0;
handleDisconnect(sd,true);
}
else
{
std::map<int, ClientConnection*>::iterator client_connection_socket_iter = m_clientConnectionSocketMap.find(sd);
if(client_connection_socket_iter != m_clientConnectionSocketMap.end())
{
client_connection_socket_iter->second->handle_message(buffer, valread);
if(client_connection_socket_iter->second->m_logoff)
{
LOG(INFO)<<"SOCKET LOGGED OFF:"<<sd;
client_socket[i]=0;
handleDisconnect(sd,true);
}
}
else
{
LOG(ERROR)<<"UNABLE TO FIND SOCKET DESCRIPTOR:"<<sd;
}
}
}
}
}catch(...)
{
LOG(ERROR)<<"EXCEPTION CATCH!!!";
}
}
}
【问题讨论】:
-
一旦完成所有资源(套接字、内存、文件等),您是否会释放它们?逐行检查代码,查找所有分配资源的位置,或为您分配一个资源(
accept、malloc、new、std::vector<...>::push_back等)并确保有一个该资源的匹配版本。 -
谢谢 Joachim,我会去找的。查看 htop 时没有内存泄漏。这也会发生,我所看到的只是没有客户端数据包的“新连接”或“断开连接”,然后它会自行清理一下,然后回到那个糟糕的状态并留在那里。
-
Each socket is its own thread坏坏坏主意。您可以使用单个线程执行此操作。查找C10K problem。线程数应该与您拥有的内核数差不多。 -
We did some code and kernel changes, and now we cant figure out whats gone wrong.: 你可以使用源代码控制回滚你的代码吗? -
“许多线程”加上“大多数事情偶尔会无缘无故停止”听起来很像您遇到了死锁问题。即线程 A 持有锁 #1 并等待获取锁 #2,而线程 B 持有锁 #2 并等待获取锁 #1,因此两者都卡住了。 (虽然有 100 多个线程,但情况可能比这更复杂)。解决方案是确保同时持有多个锁的所有线程总是以相同的顺序获取它们...说起来容易做起来难,因为并非所有的锁获取都是显式的。乔纳森避免线程的想法可能会更好。
标签: c++ linux tcp udp tcpserver