CのプログラムをC++にするには、、、ファイル名の拡張子を .c から .cxx に変えればよい、、などと言うと殴られそうなので、もう少し真面目に 考えよう。 前の単純なserver programを見ると、socket 関数で descriptor を 作成した後、それを引数とする一連の関数を使って作業を行っている。 ということは、この descriptor をメンバー変数とする class を 作ってやれば、これら一連の関数から socket descriptor の部分を 無くすことができるということである(データの隠蔽戦略)。 というわけで、この socket descriptor を隠すという戦略だけで class 化してみよう。 class 名は mysocket にしよう (namespace を使って切り分けるなら socket のままでもよい のだが、ユーザが混乱をきたさないよう念のため)。 でもって、単純なserver programで使った socket 関連の 関数を socket descriptor だけ無くして全部そのまま(C の関数の形のまま) メンバー関数にしてしまおう。なお、前述の単純なserver program には必要ないが、client programで必要となる connect を付け加えて おく。 #include <unistd.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> class mysocket { public: mysocket(); mysocket(int domain, int type, int protocol=0); virtual ~mysocket(); 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; }; こういう定義であれば、関数の本体は掲載しなくても、ほとんど想像がつくだろう。 インラインでよいぐらいだ。まあ、一応上のファイルを(多重読み込み防止を 付け加えた上で)mysocket.h として、本体を書いておくと、 (後から述べるが、実は資源割り当てを行う class を作る時の 典型的な誤りを犯しているのだが、、) #include "mysocket.h" mysocket::mysocket() { m_sd = -1; } mysocket::mysocket(int domain, int type, int protocol) { m_sd = ::socket(domain, type, protocol); } mysocket::~mysocket() { close(); } int mysocket::close() { int retval = -1; if(m_sd != -1) { retval = ::close(m_sd); m_sd = -1; } return retval; } int mysocket::connect(const struct sockaddr* addr, socklen_t len) { return ::connect(m_sd, addr, len); } int mysocket::bind(const struct sockaddr* addr, socklen_t len) { return ::bind(m_sd, addr, len); } int mysocket::listen(int backlog) { return ::listen(m_sd, backlog); } mysocket mysocket::accept(struct sockaddr* addr, socklen_t* addrlen) { mysocket tmpsock; tmpsock.m_sd = ::accept(m_sd, addr, addrlen); return tmpsock; } int mysocket::send(const void* msg, size_t len, int flags) { return ::send(m_sd, msg, len, flags); } int mysocket::recv(void* msg, size_t len, int flags) { return ::recv(m_sd, msg, len, flags); } 簡単に解説を加えておこう。
さてこれで準備は出来た!、前の単純なserver programを書き換えてみよう! #include <string.h> #include "mysocket.h" #define BACKLOG 3 #define SERVICE_PORT 8888 int main() { mysocket s(PF_INET, SOCK_STREAM); mysocket t; struct sockaddr_in sa; struct sockaddr_in ca; socklen_t calen; memset((char*)&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons( SERVICE_PORT ); sa.sin_addr.s_addr = htonl( INADDR_ANY ); if(s.bind((const struct sockaddr*)&sa,sizeof(sa)) == -1) return 1; if(s.listen(BACKLOG) == -1) return 1; while(1) { calen = sizeof(ca); t = s.accept((struct sockaddr*)&ca, &calen); t.send("Hello\r\n", 7, 0); t.close(); } return 0; } しかし、しかし、しかし、しかし、しかし、、、 コンパイルも通り実行もできるのに、、、 "メッセージが送られない!!" これは先に書いたように資源(今の場合 socket descriptor)割り当て を行う class を作る際の"典型的な誤り例"である。 これを次節"Copy constructorと代入演算子"で述べよう。 |