TCP送受信のプログラムを作成しているところですが、
スタックオーバーフローが出てしまいます。
MS visualStuio2003の出力は以下のとおりです。OSはXP pro SP1です。
TEST.exe!_chkstk() 行 91 Asm
TEST.exe!_NMSG_WRITE(int rterrnum=0x00000019) 行 194 + 0x18
TEST.exe!_amsg_exit(int rterrnum=0x00000019) 行 320 + 0x9
TEST.exe!_purecall() 行 53 + 0x7 C
TEST.exe!std::_Destroy<RecvDataBuff>(RecvDataBuff * _Ptr=0x00abcb10) 行 49 + 0xf C++
TEST.exe!std::allocator<RecvDataBuff>::destroy(RecvDataBuff * _Ptr=0x00abcb10) 行 152 + 0x9 C++
TEST.exe!std::deque<RecvDataBuff,std::allocator<RecvDataBuff> >::pop_front() 行 575 C++
TEST.exe!std::queue<RecvDataBuff,std::deque<RecvDataBuff,std::allocator<RecvDataBuff> > >::pop() 行 72 C++
TEST.exe!CSockTcp::GetRecvData() 行 260 C++
CByteArray& CSockTcp::GetRecvData()
{
m_recv.Copy( m_queue.front() );
m_queue.pop();
return m_recv;
}
TEST.exe!CXXXDlg::RecvData(PEER_KIND peer=PEER_CTL2, long size=0x0000000c) 行 656 + 0x12 C++
// 受信データ取得
☆m_recvBuf.Copy( m_sockObj[peer]->GetRecvData() );☆
原因は以下のエラーだとおもうのですが、なんでこれが出るのかがわからないのです。
rterrno=0x00000019 rterrtxt=0x00624c90 R6025 pure virtual function call
virtual function なんて呼び出してないよ><
スタックオーバーフローは関数を呼び出しすぎると起きるそうですが、
送受信のためにぐるぐる回していても問題ないですよね?
情報が足りないかもしれませんが、コードの抜粋を載せます。
(エラーのところも少し載せてますが)
class RecvDataBuff : public CByteArray
{
public:
RecvDataBuff();
RecvDataBuff(const RecvDataBuff& src);
~RecvDataBuff();
};
ソケットは、送受信用にサーバーが3個、クライアントを1個使用しています。
受信処理は以下のような感じでヘッダのサイズ情報からメッセージ分だけ受信するようにしています。
bool CSockServer::RecvData()
{
struct MIF_0001 hed = {0};
long rtn;
LPBYTE ptr = (LPBYTE)&hed;
long size = sizeof(struct MIF_0001);
RecvDataBuff tmp;
long total;
// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return false;
}
if ( rtn == SS_TIMEOUT )
{
return true;
}
ptr += rtn;
size -= rtn;
}
// 総サイズ
// total = ntohs(hed.msg_size) + sizeof(hed);
total = ntohs(hed.msg_size);
tmp.SetSize( total );
// ヘッダサイズを除くサイズ
size = total - sizeof(struct MIF_0001);
if ( size > 0 )
{
ptr = tmp.GetData();
ptr += sizeof(struct MIF_0001);
RecvData( ptr, size );
}
memcpy( tmp.GetData(), &hed, sizeof(struct MIF_0001) );
m_queue.push( tmp );
::PostMessage( m_hWnd, WM_SS_RECV, m_threadNum, total );
return true;
}
補足:queue<RecvDataBuff> m_queue;
スタックオーバーフローではないのでは?
http://support.microsoft.com/kb/125749/ja
http://msdn2.microsoft.com/ja-jp/library/wbddte9e(VS.80).aspx
この辺りのが参考になるかも...
ちなみにスタックオーバーフローの時は「stack overflow」とはっきり出ますが、それは出てます?
編集 削除こんばんは、レスありがとうございます。
stack overflowはちゃんと?出ています。
初回の例外発生〜
ブレークポイント挟みながら、デバックしていたら、R6025が発生し、F5(実行)を10回以上すると、スタックオーバーフローします。
ブレークポイントは_purecallに設定しました。
R6025のエラーが何度も出て、結果的にスタックオーバーフローになっているみたいです。
通信状態はまったく同じでも、出るときは数分〜4時間ぐらい出ないときもあります。
えてこうさんのページを見ましたけど、以下のようなことはしてないはずなんですけど。。。
>このエラーは、派生クラス型へのキャストによって作成されたポインタ (実際は基本クラスを指すポインタ) を使って抽象基本クラス中の仮想関数が呼び出されたときに発生します。また、基本クラスの構築時に生成された void* からクラスのポインタへのキャストが行われたときにも発生します。
スタックオーバーフローならスタックの使用を下げるしかありません。標準では1スレッド辺り1MBしかありませんので、それ以内に収まるようにローカル変数を減らしてください。再帰関数や複数のスレッドとして作成される関数はグローバルやstaticは使えませんからmallocやnewする必要があります。
>スタックオーバーフローは関数を呼び出しすぎると起きるそうですが、
「再帰関数」と「ある関数を複数のスレッドとして作成する」ことは違います。
>送受信のためにぐるぐる回していても問題ないですよね?
受信待ちの無限ループのことなら上記の話とも違います。色々混同してません?
というか、pure virtual function call が起こるのがそもそもおかしいです。
まずはそっちをなんとかしましょう。
RecvDataBuff のデストラクタあたりが怪しい気がします。
>bool CSockServer::RecvData()
・
>rtn = Recv( ptr, size );
・
>RecvData( ptr, size );
このあたりに疑問を感じるのだけど
ああ、ほんとだw意味の無い再帰になってる。
編集 削除すみません、モデムが壊れて書き込めませんでした。
原因はpure virtual function call がなんども起きているからだと思います。これを直せばオーバーフローはしないと思うのですが、
自作のソースには、純粋仮想関数とかは定義していないので、なぜ発生するかはまだわかっていません。
>RecvDataBuff のデストラクタあたりが怪しい気がします。
ソースは以下のとおりです。多分問題ないと
RecvDataBuff::RecvDataBuff() : CByteArray()
{
}
RecvDataBuff::RecvDataBuff(const RecvDataBuff& src)
{
this->RemoveAll();
this->Copy(src);
}
RecvDataBuff::~RecvDataBuff()
{
}
以下については、関数が違うので、再帰ではないです。
情報不足で申し訳ありません。
>bool CSockServer::RecvData()
>rtn = Recv( ptr, size );
>RecvData( ptr, size );
ソケットのクラスは
CSockTcp
CSockServer:CSockTcp
があります。
受信スレッド
Thread()
{
・
CSockServer::RecvData();
・
}
先に呼ばれる受信関数
bool CSockServer::RecvData()
{
struct MIF_0001 hed = {0};
long rtn;
LPBYTE ptr = (LPBYTE)&hed;
long size = sizeof(struct MIF_0001);
RecvDataBuff tmp;
long total;
// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return false;
}
if ( rtn == SS_TIMEOUT )
{
return true;
}
ptr += rtn;
size -= rtn;
}
// 総サイズ
// total = ntohs(hed.msg_size) + sizeof(hed);
total = ntohs(hed.msg_size);
tmp.SetSize( total );
// ヘッダサイズを除くサイズ
size = total - sizeof(struct MIF_0001);
if ( size > 0 )
{
ptr = tmp.GetData();
ptr += sizeof(struct MIF_0001);
RecvData( ptr, size );
}
memcpy( tmp.GetData(), &hed, sizeof(struct MIF_0001) );
m_queue.push( tmp );
::PostMessage( m_hWnd, WM_SS_RECV, m_threadNum, total );
return true;
}
↑の関数から呼び出す受信関数
void CSockServer::RecvData( LPBYTE buf, long size )
{
long rtn;
LPBYTE ptr = buf;
// ヘッダ部受信
while( size > 0 )
{
rtn = Recv( ptr, size );
if ( rtn < 0 && rtn != SS_TIMEOUT )
{
CloseSocket();
return;
}
if ( rtn == SS_TIMEOUT )
{
return;
}
ptr += rtn;
size -= rtn;
}
}
> Thread()
> {
> ・
> CSockServer::RecvData();
> ・
> }
RecvData()はこういう呼び出しをしているのでしょうか?
呼び出し方は、正しくは、
↓のとおりです。(CSockServer* pServer;)
bRtn = pServer->RecvData();
デバック操作を教えてもらって、確かめたところ、
m_queueにpop不可能なデータが入り、以降からpopのとこでpopできずに、
R6025が発生します。
m_queueの中は以下のようになってました。(c-_Map-[CByteArray])
m_pData 0x00000000 <不適切なPtr>
m_nSize 0x00000000
m_nMaxSize 0x00000000
m_nGrowBy 0x00000000
なぜ、m_queueにpop不可なデータが入るかまでは分かりませんでした。
pushの前でデータが正常かどうかTRACEで出すようにしましたけど、異常なデータはなかったです。(要素が0になっていないかを見ました)