我一直在尝试基于继承创建TCP服务器模型,并取得了不同的成功。这些服务器由一个单独的服务器管理,其任务是关闭这些服务器以及其他简单的维护功能:
class TCPServer {
public:
TCPServer();
~TCPServer();
void Bind(TCPDaemon *daemon) {
if(!daemon->IsRunning()) {
throw TCPBindException("Daemon is inactive");
}
// if the port is not taken, bind this daemon to it
if(this->servers.count(daemon->port())==0) {
this->servers[daemon->port()]=daemon;
...
} else {
throw TCPBindException("Port is taken");
}
}
void Shutdown() {
MASON::UINT16 i;
for(i=0;i<this->servers.size();i++) {
this->Shutdown((*this->servers.begin()).first);
}
}
void Shutdown(unsigned short port) {
if(this->servers.count(port)) {
if(this->servers[port]->IsRunning()) {
this->servers[port]->Stop();
}
delete this->servers[port];
this->servers.erase(port);
}
}
private:
std::map<unsigned short, TCPDaemon*> servers;
};
TCPDaemon类的Stop()函数是纯虚拟的。我的问题是,当调用Shutdown()函数时,它试图调用此纯虚拟虚拟而不是派生类的版本。我怎么能强迫它做正确的事呢?
提前致谢
[编辑]抱歉我以前没有包含TCPDaemon代码,它来自TCPSocket类(我已经检查过100%工作并且相当不言自明)。这是:
class TCPDaemon: public TCPSocket {
public:
TCPDaemon(unsigned short port) {
this->_enabled=false;
this->_host.ipaddr(INADDR_ANY);
this->_host.port(port);
this->paused=false;
struct sockaddr_in opts=this->_host.Compile();
#ifdef PLATFORM_WINDOWS
WSADATA wsaData;
if(WSAStartup(0x0202, &wsaData)) {
throw TCPDaemonException("Failed to start WSA");
}
#endif
this->raw_socket=socket(AF_INET, SOCK_STREAM, 0);
if(this->raw_socket<=0) {
throw TCPDaemonException("Failed to create socket");
}
if(int status=bind(this->raw_socket, (sockaddr*)&opts, sizeof(sockaddr))) {
printf("error [%i]\r\n", status);
throw TCPDaemonException("Failed to bind to port");
}
if(listen(this->raw_socket, 5)) {
throw TCPDaemonException("Failed to listen on port");
}
this->_enabled=true;
}
virtual ~TCPDaemon() {
this->Shutdown();
}
virtual void Start()=0;
virtual void Run(TCPSocket*)=0;
virtual void Stop()=0;
unsigned short port() {
return this->host().port();
}
bool IsRunning() {
return this->_enabled;
}
TCPSocket *Accept() {
SOCKET client;
struct sockaddr client_addr;
int len=sizeof(client_addr);
client=accept(this->raw_socket, &client_addr, &len);
return new TCPSocket(client, &client_addr);
}
void Shutdown() {
}
private:
bool _enabled;
bool paused;
};
这是一个示例派生服务器及其创建方法:
class EchoServer: public TCPDaemon {
public:
EchoServer(MASON::UINT16 port): TCPDaemon(port) {
}
~EchoServer() {}
virtual void Start() {
}
virtual void Run(TCPSocket *client) {
printf("RUN\r\n");
Accessor<TCPSocket> acc_client=client;
acc_client->Write(Accessor<Blob> (new Blob(std::string("hello!"))));
acc_client->Disconnect();
}
virtual void Stop() {
}
};
myTCPServer->Bind(new EchoServer(8008));
[编辑+ 1]我认为问题归结为这(我可能很容易出错): 我有一个基类的std :: map,TCPDaemon,它有一个纯虚拟/抽象函数Stop()。看来当我通过地图中的一个条目调用Stop()时,它试图调用TCPDaemon :: Stop(),而不是重写函数EchoServer :: Stop()。这可能是问题吗?如果是,我该如何解决?
答案 0 :(得分:1)
检查您声明的语法:
class TCPDaemon
{
virtual void stop() = 0;
};
class MyDaemon : public TCPDaemon
{
virtual void stop()
{
//Do stuff here.
}
};
如果没有更多代码,这是我能做的最好的事情。
编辑:
好的,好像你正在使用抽象函数。接下来的问题是:你得到的错误是什么?我可以肯定地说,不试图从TCPDeamon调用Stop()。这是不可能的,因为它甚至没有实现。
答案 1 :(得分:1)
由于我收到的意见,我终于完成了它。问题在于TCPServer :: Shutdown(unsigned short)中的删除调用,这导致代码完全不同的部分中存在内存访问冲突......一个相当noobie错误,我将尽快包装智能指针。
感谢您的所有反馈!
答案 2 :(得分:0)
如果你说它调用纯虚方法而不是派生类'版本,那意味着派生类调用它。 E.g。
class t1
{
public:
virtual ~t1(){};
virtual void foo()=0
{
std::cout << "pure virtual";
};
};
class t2:public t1
{
public :
virtual void foo()
{
t1::foo();
std::cout << "derived class ";
};
};
据我所知,只有派生类'对象可以调用基类'纯虚函数(当然,如果它是在基类中实现的)
答案 3 :(得分:0)
我不确定这是否是您所看到的问题,但以下功能肯定存在问题。
void Shutdown() {
MASON::UINT16 i;
for(i=0;i<this->servers.size();i++) {
this->Shutdown((*this->servers.begin()).first);
}
}
在每个循环迭代器之后,i
将增加一个,size
将减少,因此您只会将服务器的一半关闭。也许保持开放是造成进一步错误的原因。
您可以这样做:
void Shutdown() {
MASON::UINT16 i;
std::size_t nrServers = this->servers.size();
for(i=0;i<nrServers;i++) {
this->Shutdown((*this->servers.begin()).first);
}
}
或者,我更喜欢它,因为它更好地显示了代码的意图:
void Shutdown() {
while (!this->servers.empty()) {
this->Shutdown((*this->servers.begin()).first);
}
}