1

これは、このサイトでの最初の質問であり、C++ コーディングで何度も使用してきました。

私が質問しているのは、私が壁にぶつかったからです。何が起こっているのか理解できず、誰かが解決するのは素敵な小さな謎だと思います。

1 つのサーバーと複数のクライアントを備えたクライアント/サーバー アーキテクチャがあります。

以下のコード スニペットは、ブロックする唯一のソケットが mList の最後のソケットであることを除けば、正常に動作します。そのため、2 つのクライアントが接続されている場合、接続された 2 番目のソケットのみがデータを受信します。このコードは私自身のものではありません。if ステートメントを移動するときに発生するセグメンテーション違反をデバッグしようとしています。

int SocketManager::block(int secs, int usecs)
{
int ret = 0;
int result = 0;
struct timeval tv;
fd_set set;
int max_sd = 0;

if (mList.empty())
{
  return -1;
}

tv.tv_sec = secs;
tv.tv_usec = usecs;

FD_ZERO(&set);

SocketList::const_iterator iter;
int n = 0;

for (iter = mList.begin(); iter != mList.end(); iter++)
{
  if ((*iter)->socket() > max_sd)
  {
    max_sd = (*iter)->socket();
  }

  FD_SET((*iter)->socket(), &set);
  n++;
}

errno = 0;
if ((result = select(max_sd + 1, &set, NULL, NULL, &tv)) > 0)
{
  ret = 1;
  Socket *s = NULL;
  for (iter = mList.begin(); iter != mList.end(); iter++)
  {
    if (FD_ISSET((*iter)->socket(), &set))
    {
      s = (*iter);
    }
  }

  if (s)
  {
    mLastSocketPtr = s;
    s->checkForData();
    mLastSocketPtr = NULL;
  }
}
else if (result == 0)
{
  // Timeout
  ret = 0;
}
else
{
  // Error
  ret = -1;
}

return ret;
}

この問題は、if ステートメントが「if (FD_ISSET((*iter)->socket(), &set))」の行で発生します。

if (s)
{
  mLastSocketPtr = s;
  s->checkForData();
  mLastSocketPtr = NULL;
}

FD_ISSET関数内に移動されます

for (iter = mList.begin(); iter != mList.end(); iter++)
      {
        if (FD_ISSET((*iter)->socket(), &set))
        {
          s = (*iter);
          if (s)
          {
            mLastSocketPtr = s;
            s->checkForData();
            mLastSocketPtr = NULL;
          }
        }
      }

何か助けはありますか?

4

1 に答える 1

0

ほとんどの場合、問題はmListトラバース中に変更していることです。ループcheckForData内に移動すると、インクリメントおよび逆参照しようとしている反復子が無効になるため、内部への変更は許可されません。formListcheckForData

このコードを修正する方法はたくさんあります。そもそもこのようなコードを書かないことが最善の方法ですが、船は出航したと思います。以下にいくつかの提案を示します。

1) を通過するFD_SET代わりに を通過しmListます。で見つかった各ヒットについてFD_SET、対応するエントリを で見つけますmList。そうすれば、変わることのない何かを経験することになります。

2) でヒットしたら、FD_SETでフラグをクリアしFD_SETます。forへの新たな呼び出しでループを開始しますbegin

3) リストを一度vector調べて、処理が必要なすべてのソケットを作成します。次に、そのベクトルを反復処理して を呼び出しますcheckForData

方法 2 を示す醜い修正を次に示します。

for (iter = mList.begin(); iter != mList.end(); iter++)
  {
    if (FD_ISSET((*iter)->socket(), &set))
    {
      s = (*iter);
      FD_CLR(s->socket(), &set); // don't find this again
      mLastSocketPtr = s;
      s->checkForData();
      mLastSocketPtr = NULL;
      iter = mList.begin(); // make the iterator valid
      continue; // don't increment the iterator
    }
  }
于 2012-10-08T09:09:44.073 に答える