TCP / IP ネットワークプログラミング - 2 / 3 -
TCP / IP ネットワークプログラミングの処理フロー
本アプリの処理フローです。ボックスに記された TCPConnect, TCPDNSConnect, TCPConnected, TCPWrite, TCPRead, Draw はサンプルコードの関数に対応します。

IP アドレスの取得
通信先の ホスト名 から IP アドレスを取得するために、DNS サーバに問い合わせます。
TCPConnect 関数は、最初に CALLBACK_Init マクロ関数で 第 2 引数の TCPDNSConnect をコールバック関数として pApp->callback という AEECallback 構造体を初期化します。
次に INETMGR_GetHostByName 関数を使って DNS サーバからIP アドレスを取得し、AEEDNSResult 構造体の変数 pApp->dnsresult に格納します。
#define ACCESS_URL "www.google.com" //どの URL でも構いません
// ホスト名からIPアドレスを調べる
static void TCPConnect(SocketApp* pApp)
{
CALLBACK_Init(&pApp->callback, TCPDNSConnect, pApp);
INETMGR_GetHostByName(pApp->netmgr, &pApp->dnsresult,
ACCESS_URL, &pApp->callback);
return;
}
AEEDNSResult 構造体は次のように定義されす。AEEDNSResult 構造体の変数 pApp->dnsresult では、nResult フィールドには取得した IP アドレスの個数、addrs[] フィールドには取得した IP アドレスのリストが保持されます。
typedef struct
{
int nResult;
INAddr addrs[AEEDNSMAXADDRS];
} AEEDNSResult;
ソケット作成・接続
IP アドレスを取得すると、コールバック関数 TCPDNSConnect に処理が移ります。
TCPDNSConnect 関数では、INETMGR_OpenSocket 関数を使って ISocket のインスタンスを生成し、ソケットを開き、ホストへ接続します。
少々時間のかかるホスト接続の処理 TCPConnected はコールバック関数として登録します。
// ホストに接続する。
static void TCPDNSConnect(void* p)
{
SocketApp* pApp = (SocketApp*)p;
int nErr = pApp->dnsresult.nResult;
if (nErr > AEEDNSMAXADDRS) {
// IP アドレスの取得に失敗。nErr の値によって原因がわかる。
DBGPRINTF("** DNS Lookup Failed: Error %d\n", nErr);
}
else {
pApp->socket = INETMGR_OpenSocket(pApp->netmgr, AEE_SOCK_STREAM);
if (pApp->socket == NULL) {
DBGPRINTF("** OpenSocket Failed: Error %d\n",
INETMGR_GetLastError(pApp->netmgr));
}
else {
// pApp->dnsresult.addrs[0] に取得した IP アドレスが保持される。
// HTONS は、ポート番号を通信用に変換するマクロ関数。
nErr = ISOCKET_Connect(pApp->socket, pApp->dnsresult.addrs[0],
HTONS(80), TCPConnected, pApp);
if (nErr != AEE_NET_SUCCESS) {
DBGPRINTF("** Connect Failed: Error %d\n",
ISOCKET_GetLastError(pApp->socket));
ISOCKET_Cancel(pApp->socket, NULL, NULL);
ISOCKET_Release(pApp->socket);
pApp->socket = NULL;
}
else {
DBGPRINTF("** connecting...\n");
}
}
}
return;
}
メッセージの送信
TCPConnected 関数でホスト接続したら、TCPWrite 関数でメッセージを送信します。
// メッセージの送信を開始する。
static void TCPConnected(void* p, int nErr)
{
SocketApp* pApp = (SocketApp*)p;
if ((nErr == AEE_NET_SUCCESS) || (nErr == AEE_NET_EISCONN)) {
// エラーがなければメッセージを送信 (TCPWrite 関数)
pApp->index = 0;
TCPWrite(pApp);
}
else {
DBGPRINTF("** Connect Failed: Error %d\n", nErr);
ISOCKET_Cancel(pApp->socket, NULL, NULL);
ISOCKET_Release(pApp->socket);
pApp->socket = NULL;
}
return;
}
TCPWrite 関数は、"GET / HTTP/1.0¥n¥n" というリクエストを HTTP サーバに送信します。
その後、TCPRead 関数はトップページを受信します。
ソケットプログラミングでは、送受信するメッセージの大きさが予測できません。
そのため、TCPWrite 関数と TCPRead 関数では、ぞれぞれ自分自身である TCPWrite と TCPRead をコールバック関数として登録し、完了するまでメッセージの送受信を繰り返します。
// メッセージを送信、完了すれば受信を開始する。
static void TCPWrite(SocketApp* pApp)
{
int nSent;
char message[] = "GET / HTTP/1.0\n\n";
// メッセージの送信。返値は実際に送信したバイト数 (もしくはエラーコード)
// 第三引数はバッファのサイズ
nSent = ISOCKET_Write(pApp->socket, (byte *)message + pApp->index,
(uint16)sizeof(message) - pApp->index);
if (nSent == AEE_NET_ERROR) {
DBGPRINTF("** Write Failed: Error %d\n",
ISOCKET_GetLastError(pApp->socket));
ISOCKET_Cancel(pApp->socket, NULL, NULL);
ISOCKET_Release(pApp->socket);
pApp->socket = NULL;
}
else {
// nSent が AEE_NET_WOULDBLOCK の場合は、メッセージが送信できなかった。
if (nSent != AEE_NET_WOULDBLOCK) {
pApp->index += nSent;
if (pApp->index >= sizeof(message)) {
// 送信終了
DBGPRINTF("** writing complete...\n");
// 受信開始
TCPRead(pApp);
return;
}
}
DBGPRINTF("** writing...\n");
// 送信が完了していないときは、
// 再びコールバック関数(この関数自身)を登録する。
ISOCKET_Writeable(pApp->socket, (PFNNOTIFY)TCPWrite, pApp);
}
return;
}
メッセージの受信
全てのメッセージの受信が完了したら、ISOCKET_Release 関数を呼び出してソケットを閉じます。
最後に、Draw 関数を呼び出して受信したメッセージを画面に表示します。
// メッセージを受信、完了すればリソースを開放し画面に描画する。
static void TCPRead(SocketApp * pApp)
{
char buffer[0x7FFF];
int nRead;
// メッセージの受信。返値は実際に受信したバイト数 (もしくはエラーコード)
// 第三引数はバッファのサイズ
nRead = ISOCKET_Read(pApp->socket, (byte*)buffer, 0x7FFF - 1);
if (nRead == AEE_NET_ERROR) {
DBGPRINTF("** Read Failed: Error %d\n",
ISOCKET_GetLastError(pApp->socket));
ISOCKET_Cancel(pApp->socket, NULL, NULL);
ISOCKET_Release(pApp->socket);
pApp->socket = NULL;
}
else {
// nRead が AEE_NET_WOULDBLOCK の場合は、メッセージが受信できなかった。
if (nRead != AEE_NET_WOULDBLOCK) {
if (nRead == 0) {
// 受信終了
DBGPRINTF("** reading Complete...\n");
ISOCKET_Release(pApp->socket);
pApp->socket = NULL;
return;
}
else {
// バッファの終端に '\0' を代入。
buffer[nRead] = 0;
// 文字列の画面描画
COMMON_WriteString(&pApp->common, buffer);
COMMON_Draw(&pApp->common);
}
}
DBGPRINTF("** reading...\n");
// 受信が完了していないときは、再びコールバック関数(この関数自身)を登録
ISOCKET_Readable(pApp->socket, (PFNNOTIFY)TCPRead, pApp);
}
return;
}

















