スタックオーバーフローにならいようにするためには?


vein  2008-01-22 10:49:45  No: 67348

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;


えてこう  2008-01-23 01:20:10  No: 67349

スタックオーバーフローではないのでは?

http://support.microsoft.com/kb/125749/ja
http://msdn2.microsoft.com/ja-jp/library/wbddte9e(VS.80).aspx

この辺りのが参考になるかも...


  2008-01-23 04:00:45  No: 67350

ちなみにスタックオーバーフローの時は「stack overflow」とはっきり出ますが、それは出てます?


vein  2008-01-23 10:05:18  No: 67351

こんばんは、レスありがとうございます。

stack overflowはちゃんと?出ています。
初回の例外発生〜

ブレークポイント挟みながら、デバックしていたら、R6025が発生し、F5(実行)を10回以上すると、スタックオーバーフローします。

ブレークポイントは_purecallに設定しました。

R6025のエラーが何度も出て、結果的にスタックオーバーフローになっているみたいです。

通信状態はまったく同じでも、出るときは数分〜4時間ぐらい出ないときもあります。

えてこうさんのページを見ましたけど、以下のようなことはしてないはずなんですけど。。。
>このエラーは、派生クラス型へのキャストによって作成されたポインタ (実際は基本クラスを指すポインタ) を使って抽象基本クラス中の仮想関数が呼び出されたときに発生します。また、基本クラスの構築時に生成された void* からクラスのポインタへのキャストが行われたときにも発生します。


  2008-01-25 03:22:27  No: 67352

スタックオーバーフローならスタックの使用を下げるしかありません。標準では1スレッド辺り1MBしかありませんので、それ以内に収まるようにローカル変数を減らしてください。再帰関数や複数のスレッドとして作成される関数はグローバルやstaticは使えませんからmallocやnewする必要があります。

>スタックオーバーフローは関数を呼び出しすぎると起きるそうですが、
「再帰関数」と「ある関数を複数のスレッドとして作成する」ことは違います。

>送受信のためにぐるぐる回していても問題ないですよね?
受信待ちの無限ループのことなら上記の話とも違います。色々混同してません?


シャノン  2008-01-25 04:07:30  No: 67353

というか、pure virtual function call が起こるのがそもそもおかしいです。
まずはそっちをなんとかしましょう。
RecvDataBuff のデストラクタあたりが怪しい気がします。


rin  2008-01-26 09:32:19  No: 67354

>bool CSockServer::RecvData()

>rtn = Recv( ptr, size );

>RecvData( ptr, size );
このあたりに疑問を感じるのだけど


  2008-01-26 10:33:10  No: 67355

ああ、ほんとだw意味の無い再帰になってる。


vein  2008-01-29 06:12:05  No: 67356

すみません、モデムが壊れて書き込めませんでした。

原因は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;
  }
}


赤猿  2008-01-30 22:08:58  No: 67357

> Thread()
> {
> ・
> CSockServer::RecvData();
> ・
> }

RecvData()はこういう呼び出しをしているのでしょうか?


vein  2008-02-02 22:36:08  No: 67358

呼び出し方は、正しくは、
↓のとおりです。(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になっていないかを見ました)


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加