←戻る

クラス設計を考える

材料

前回の記事(「NTPを使ってみよう」)を使用します。

FCNTPクラスを検証する

まず、portabilityの側面から見て、FCNTPクラスの問題点を洗い出します。

他にもいろいろありそうな気がするんだけどなぁ…

設計の見直し

とりあえず、検証で出てきた問題点を以下の作業の流れになるように修正していきます

コーディング

とりあえず、クラスを利用する層のコードを考えてみる

#include "FCNTP.h"
#include <winsock2.h>

#include <iostream>
#include <exception>

int main()
{
	WSADATA wsad;
	short int wVersion;
	int err;
	FCNTP NTP;
	unsigned long ulTime;

	wVersion = MAKEWORD(2, 0);

	err = WSAStartup(wVersion, &wsad); // Winsockスタート
	if (err != 0) {
		std::cerr << "WSAStartup()失敗したYO(Д" << std::rndl;
		exit(1);
	}

	// 要求したバージョンが使用可能かどうか調べる
	if (HIWORD(wVersion) != HIWORD(wsad.wVersion) || LOWORD(wVersion) != LOWORD(wsad.wVersion)) {
		WSACleanup();
		std::cerr << "要求したバージョン使えねーじゃん(Д" << std::endl;
		exit(1);
	}

	try {
		NTP.socket();
		NTP.connect("127.0.0.1"); // ここは適当にNTPサーバのIPアドレスを入れる
		NTP.recv(&ulTime);
		NTP.close();
	} catch (std::exception &e) {
		std::cerr << e.what() << std::endl;
		exit(1);
	}

	std::cout << "現在、1900/01/01-00:00:00から数えて " << ulTime << " 秒後です。" << std::endl;

	WSACleanup();

	return 0;
}

unixの場合ならwinsockの操作は要らないので以下のようになります

#include "FCNTP.h"

#include <iostream>
#include <exception>

int main()
{
	FCNTP NTP;
	unsigned long ulTime;

	try {
		NTP.socket();
		NTP.connect("127.0.0.1"); // ここは適当にNTPサーバのIPアドレスを入れる
		NTP.recv(&ulTime);
		NTP.close();
	} catch (std::exception &e) {
		std::cerr << e.what() << std::endl;
		exit(1);
	}

	std::cout << "現在、1900/01/01-00:00:00から数えて " << ulTime << " 秒後です。" << std::endl;

	return 0;
}

次に、FCNTPクラスを考えてみます。

// ---------------------------- FCNTP.h ---------------------------------------
#if defined _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif

#include <string.h>
#include <exception>

class FCNTP_EXCP : public exception // 例外クラス
{
public:
	FCNTP_EXCP() throw () {m_strData = "Unknown Exception"; }
	FCNTP_EXCP(const char *strException) throw (){m_strData = strException; }

	const char *what() throw() { return m_strData; }

private:
	const char *m_strData;
};

class FCNTP {
public:
	FCNTP();
	~FCNTP();
	int socket(void) throw (FCNTP_EXCP);
	int connect(const char *strServerIP) throw (FCNTP_EXCP);
	int recv(unsigned long *pulTime) throw (FCNTP_EXCP);
	int close(void);

	int _s;
};

//---------------------------- FCNTP.cpp --------------------------------------
#include "FCNTP.h"

FCNTP::FCNTP()
{
	_s = -1;
}

FCNTP::~FCNTP()
{
	close();
}

int FCNTP::socket() throw (FCNTP_EXCP)
{
	_s = ::socket(AF_INET, SOCK_STREAM, 0);
	if (_s == -1) {
		throw FCNTP_EXCP("使えねーじゃん");
	}
	
	return 0;
}

int FCNTP::connect(const char *strServerIP) throw (FCNTP_EXCP)
{
	unsigned long ulAddr;
	unsigned short usPort;
	sockaddr sa;

	memset(&sa, 0, sizeof(sa));
	sa.sa_family = AF_INET;
	usPort = htons(37); // ポート番号をネットワークバイトオーダーに変換
	ulAddr = inet_addr(strServerIP);

	memmove(sa.sa_data, &usPort, 2);
	memmove(sa.sa_data + 2, &ulAddr, 4); 

	if (::connect(_s, &sa, sizeof(sa)) == -1) {
		throw FCNTP_EXCP("繋がんねーじゃん(Д");
	}
	
	return 0;
}

int FCNTP::recv(unsigned long *pulTime) throw (FCNTP_EXCP)
{
	unsigned long ulRVal = 0L;
	if (::recv(_s, reinterpret_cast<char *>(&ulRVal), 4, 0) == -1) {
		throw FCNTP_EXCP("受信できねーぞ(Д");
	}
	*pulTime = ntohl(ulRVal); // 返り値をネットワークバイトオーダーから変換
	
	return 0;
}

int FCNTP::close()
{
#if defined  _WIN32
	::shutdown(_s, SD_BOTH); 
	::closesocket(_s);
#else
	::shutdown(_s, SHUT_RDWR); 
	::close(_s);
#endif

	return 0;
}

ふむ、こんなもんですかね。 (#define _WIN32を使ってるとか使用してる構造体が前回と違ってたりとかするのは、今回はcygwin上のg++(2.95.3-5)でもコンパイル確認しているからです。ちなみにVC++のコンパイラはCL.EXE(12.00.8804)です。)


←戻る

Valid HTML 4.01!

Valid CSS!