C++による単純なserver program

前節のC++による単純なserver program典型的な誤り例であると述べた。 ここではこの問題を考察しよう。

どこが誤りであるかというと、mysocket class で copy constructor と代入演算子が定義 されていない、すなわち default のそれらが使われている点である。 default では各メンバーデータはそのまま代入される。つまり socket descriptor は int なので、int の代入が起こる。

しかし socket descriptor は socket object を int で表現している だけであって数学上の int ではない。四則演算ができるわけではない。 代入もしかり。単純に同じ値にすればよいというものではない。

前節の例では accept() でこの問題が起こっている。accept() は 受け入れた新しい mysocket object を返す。 accept関数の中では、mysocket object である tmpsock を 用意し、このデータメンバーを設定して tmpsock を戻り値にして いる。すなわち tmpsock を呼び出し側 stack へコピーして 関数は終了し、そこから呼び出し側に用意された空の mysocket t へ 代入される。

では tmpsock はどうなるだろう?関数から抜ける時点で scope から 抜けるので destructor が呼ばれる。destructor は socket descriptor を close する。今仮に accept した socket descriptor が 5 だとすると、 5 が closeされる。にもかかわらず 5 は呼び出し側の t の socket descriptor の 値でもある。つまり t という object は以後 close された 5 に対し 操作を行うことになる。これが動作しない理由である。

では、この問題の解決はどうすればよいか? システムにはこのような目的のために descriptor を複写する関数が 用意されている。これを使わなければならない。 具体的には dup関数(Windows の場合は DuplicateHandle?関数)を 使う。

dup関数は、同一実体に対し、異なる descriptor 値を割り当てて くれる。dup関数を使えばシステムは同一実体に対し、 複数の descriptor を与えたことを認識してくれるので、 一方を close しても実体そのものは解放しない。 割り当てたすべての descriptor が close された時点で初めて実体そのものも 解放する。従って、例えば descriptor 5 に対し、dup により 7 が与えられた とすると、5 を close しても 7 は使える。

というわけで、copy constructor と代入演算子を宣言する。

#ifndef MYSOCKET_H_INCLUDED
#define MYSOCKET_H_INCLUDED

#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

class mysocket
{
public:
  mysocket();
  mysocket(const mysocket& from);
  mysocket(int domain, int type, int protocol=0);
  virtual ~mysocket();
  virtual mysocket& operator=(const mysocket& from);
  virtual int close();
  virtual int connect(const struct sockaddr* addr, socklen_t len);
  virtual int bind(const struct sockaddr* addr, socklen_t len);
  virtual int listen(int backlog);
  virtual mysocket accept(struct sockaddr* addr, socklen_t* addrlen);
  virtual int send(const void* msg, size_t len, int flags=0);
  virtual int recv(void* msg, size_t len, int flags=0);

protected:
  int m_sd;
};

#endif

つけ加わった copy constructor と代入演算子は dup を使って

mysocket::mysocket(const mysocket& from)
{
  m_sd = -1;
  if(from.m_sd != -1)
    m_sd = ::dup(from.m_sd);
}

mysocket& mysocket::operator=(const mysocket& from)
{
  if(&from == this)
    return *this;
  close();
  if(from.m_sd != -1)
    m_sd = ::dup(from.m_sd);
  return *this;
}

と書ける。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2006-06-06 (火) 21:59:52