2020年11月アーカイブ

1回きりのデータ送受信では、TCP/IPによるソケット通信とは呼べない? という事で、ウィンドウ上で繰り返しデータの送受信を行ってみる事に。

試しに、ボタン1でWinSockの起動、ボタン3でエディットボックスに入力した文字列を相手に送信するだけのサンプルを作ってみました。


<注意点>

・エラー処理は手抜きで済ませています。

・接続要求待ちの accept で新たに生成されたソケットを、クライアントとのデータ送受信に使用します。

・accept に使用したソケットは、何度でも接続要求待ちに利用することが出来ます。(複数のクライアントに対応可能)

・send もブロッキング動作するようですが、送信完了する(TCPの送信バッファにデータを書き終わる)までなので、気付かなかった事に。


【WM_APPについて】

ウィンドウのメッセージループへユーザ独自のメッセージを送る際に、良く見かけるのが WM_USER + xyz の定義ですが WM_USER はユーザの為では無く、定義済みのウィンドウ(コントロール)クラスの為に用意されたものです。このプライベートウィンドウクラスの範囲で、ユーザアプリケーションは重要な独自メッセージを定義してはいけません。

WM_APP は、ユーザアプリケーションが独自メッセージを利用出来るよう用意されたもので、システム定義のメッセージと競合する事は有りません。


【Windowメッセージの範囲】

Windowメッセージコードの範囲
種類範囲説明
WM_XYZXYZ0~0x3FFシステムが使用するメッセージ
WM_USER0x400~0x7FFFプライベートウィンドウクラスが使用するメッセージ *1
WM_APP0x8000~0xBFFFアプリケーションが使用できるメッセージ *2
-- 0xC000~0xFFFFRegisterWindowMessage 関数が定義するメッセージ *3
RESERVED0x10000~将来のために予約

 *1:定義済みウィンドウ(コントロール)クラス用、*2:ユーザ定義用
 *3:アプリケーション実行中に関数を呼び出し、文字列のメッセージコード取得時に定義される


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

#define WM_WS_RECV (WM_APP + 1)

static SOCKET ss, cs;  // ss - server socket, cs - client socket

unsigned WINAPI subThread2(void *hCtr)
{
    int i, len;
    char str[64], buf[64], *datpt;

    if(send(cs, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    while(1) {
        memset(buf, 0, sizeof(buf));
        if((len = recv(cs, buf, sizeof(buf), 0)) <= 0) {
            printf("recv error : %d\n", WSAGetLastError());
            WSACleanup();
            return(1);
        }
        sprintf(str, "len = %d, data = %s", len, buf);
        MessageBox(NULL, str, "クライアント送信データ", MB_OK | MB_ICONINFORMATION);

        datpt = calloc(len + 1, sizeof(char));
        for(i = 0; i < len; i++) {
            datpt[i] = buf[i];
        }
        PostMessage((HWND)hCtr, WM_WS_RECV, (WPARAM)len, (LPARAM)datpt);
    }

    return(0);
}

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char str[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((ss = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(ss, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求待ち
    if(listen(ss, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((cs = accept(ss, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    } else {
        sprintf(str, "クライアントアドレス = %s、ポート = %d", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        MessageBox(NULL, str, "接続要求受付", MB_OK | MB_ICONINFORMATION);
    }

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1, hBtn3, hEdit;
    HINSTANCE hInstance;
    int len, wmid, wmevent;
    char str[64], buf[64];

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 100, 100, 210, 30, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, hWnd, 0, NULL);
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "押下3");
                memset(buf, 0, sizeof(buf));
                len = SendMessage((HWND)hEdit, WM_GETTEXT, (WPARAM)64, (LPARAM)buf);
                if(send(cs, buf, len, 0) < 1) {
                    printf("send error : %d\n", WSAGetLastError());
                    WSACleanup();
                    return(1);
                }
                SetWindowText(hBtn3, "ボタン3");
                EnableWindow(hBtn3, TRUE);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_WS_RECV :
        sprintf(str, "len = %d, data = %s", (int)wParam, (char *)lParam);
        MessageBox(NULL, str, "クライアントからデータが送られてきました。", MB_OK | MB_ICONINFORMATION);
        if((int)wParam == 4 && strncmp((char *)lParam, "exit", 4) == 0) {
            // WinSock 切断
            shutdown(ss, SD_BOTH);
            // ソケットの破棄
            closesocket(ss);
            closesocket(cs);
            // WinSock のリソース解放
            WSACleanup();
        }
        free((char *)lParam);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

#define WM_WS_RECV (WM_APP + 1)

static SOCKET cs;  // cs - client socket

unsigned WINAPI subThread2(void *hCtr)
{
    int i, len;
    char str[64], buf[64], *datpt;

    if(send(cs, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    while(1) {
        memset(buf, 0, sizeof(buf));
        if((len = recv(cs, buf, sizeof(buf), 0)) <= 0) {
            printf("recv error : %d\n", WSAGetLastError());
            WSACleanup();
            return(1);
        }
        sprintf(str, "len = %d, data = %s", len, buf);
        MessageBox(NULL, str, "サーバ送信データ", MB_OK | MB_ICONINFORMATION);

        datpt = calloc(len + 1, sizeof(char));
        for(i = 0; i < len; i++) {
            datpt[i] = buf[i];
        }
        PostMessage((HWND)hCtr, WM_WS_RECV, (WPARAM)len, (LPARAM)datpt);
    }

    return(0);
}

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKADDR_IN sa; // sa - server address
    int i;
    char str[64];
//  char *argv = "localhost";
    char *argv = "127.0.0.1";

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    // ソケットの作成
    if((cs = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv)) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv);
            WSACleanup();
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            sprintf(str, "ホスト名 = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
        MessageBox(NULL, "ホスト名", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            sprintf(str, "アドレス = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
        MessageBox(NULL, "アドレス", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    }

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1, hBtn3, hEdit;
    HINSTANCE hInstance;
    int len, wmid, wmevent;
    char str[64], buf[64];

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 100, 100, 210, 30, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, hWnd, 0, NULL);
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "押下3");
                memset(buf, 0, sizeof(buf));
                len = SendMessage((HWND)hEdit, WM_GETTEXT, (WPARAM)64, (LPARAM)buf);
                if(send(cs, buf, len, 0) < 1) {
                    printf("send error : %d\n", WSAGetLastError());
                    WSACleanup();
                    return(1);
                }
                SetWindowText(hBtn3, "ボタン3");
                EnableWindow(hBtn3, TRUE);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_WS_RECV :
        sprintf(str, "len = %d, data = %s", (int)wParam, (char *)lParam);
        MessageBox(NULL, str, "サーバからの送信データです。", MB_OK | MB_ICONINFORMATION);
        if((int)wParam == 3 && strncmp((char *)lParam, "bye", 3) == 0) {
            // ソケットの破棄
            closesocket(cs);
            // WinSock のリソース解放
            WSACleanup();
        }
        free((char *)lParam);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

WindowsのGUI(ウィンドウ)プログラムでTCP/IP通信を利用する際、アプリケーションが固まったように見えるブロッキング動作が問題となることが有ります。

ずぼらのめんどくさがり屋には、メインスレッドと並列処理可能なサブスレッドが単純明快で打って付けかと。

試しに、WinSockの通信(データの送受信)部分をそっくり其の儘、サブスレッドに突っ込んだだけのサンプルを作ってみました。

ブロッキング動作をする accept, send, recv がサブスレッド上で動作するので、ウィンドウの最低限の動作(メインスレッドが担当)は、滞り無く行われるはずです。


<注意点>

・WSACleanup APIが呼び出されると、プロセス内の全てのスレッドによって発行されたあらゆる保留中のブロッキングや非同期WinSock呼び出しがキャンセルされます。

・WSACleanup APIが呼び出された時点でオープンされていたソケットはリセットされ、closesocket を呼び出した場合と同じように自動的に解放されます。

・全ての保留中データが確実に送信されるようにするには、アプリケーションは shutdown を使用してコネクションをクローズし、クローズ完了まで待機してから closesocket および WSACleanup を呼び出します。

・サーバを終了した直後に再度サーバを起動しようとすると、bind エラーで終了する事が有ります。この現象は、サーバ側で先にクローズを実行すると発生する事が確認されています。先にクライアント側でクローズを実行する事で、このエラーを回避出来るようです。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    SOCKET ss, cs;  // ss - server socket, cs - client socket
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char str[64];
    char buf[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((ss = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(ss, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求待ち
    if(listen(ss, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((cs = accept(ss, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    } else {
//      printf("接続要求受付:クライアントアドレス = %s、ポート = %d\n", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        sprintf(str, "クライアントアドレス = %s、ポート = %d", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        MessageBox(NULL, str, "接続要求受付", MB_OK | MB_ICONINFORMATION);
    }

    if(send(cs, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(cs, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }
//  printf("From Client : len = %d, data = %s\n", len, buf);
    sprintf(str, "len = %d, data = %s", len, buf);
    MessageBox(NULL, str, "クライアント送信データ", MB_OK | MB_ICONINFORMATION);

    // WinSock 切断
    shutdown(ss, SD_BOTH);

    // ソケットの破棄
    closesocket(ss);
    closesocket(cs);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1;
    HINSTANCE hInstance;
    int wmid, wmevent;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, NULL, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKET cs;  // cs - client socket
    SOCKADDR_IN sa; // sa - server address
    int i, len;
    char str[64];
    char buf[64];
    char *argv = "localhost";
//  char *argv = "127.0.0.1";

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    // ソケットの作成
    if((cs = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv)) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv);
            WSACleanup();
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            printf("connect1 error : %d\n", WSAGetLastError());
            sprintf(str, "ホスト名 = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
//      printf("ホスト名でのサーバ接続完了\n");
        MessageBox(NULL, "ホスト名", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            printf("connect2 error : %d\n", WSAGetLastError());
            sprintf(str, "アドレス = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
//      printf("アドレスでのサーバ接続完了\n");
        MessageBox(NULL, "アドレス", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(cs, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }
//  printf("From Server : len = %d, data = %s\n", len, buf);
    sprintf(str, "len = %d, data = %s", len, buf);
    MessageBox(NULL, str, "サーバ送信データ", MB_OK | MB_ICONINFORMATION);

    if(send(cs, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットの破棄
    closesocket(cs);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1;
    HINSTANCE hInstance;
    int wmid, wmevent;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, NULL, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

TCP/IP通信と言うと、サーバ・クライアントシステムを思い浮かべますが、行き成り外部とデータの遣り取りをするのは危なっかしい気がしますので、テスト環境にも利用される(?)同一パソコン内のプロセス間通信(ソケット通信)を試してみる事に。

試しに、WinSockAPIを使って、内部プロセス間通信(データの送受信)を行うだけのサンプルを作ってみました。

sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;

sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;

上の2つの代入文は、どちらも同じ値を代入します。分かり易い(ピンときた)方を選ぶと良いかも。

因みに、非常に難解なTCP/IP通信をぎゅっと短く平たく(間違いを恐れず)大雑把に言うと、通信する相手<パソコン>をIPアドレスを使って特定する役割を担うのがIP、通信する相手の種類<アプリケーション>をポート番号を使って指定する役割を担うのがTCP、2つ合わせて正確無比な通信をインターネット上で実現しています。


<注意点>

・WinSockAPIの accept, send, recv は、通常ブロッキング動作をします。Windows ではノンブロッキング動作にすることも出来るようですが、select を使った監視処理の方がお勧め(?)のようです。

・ブロッキング動作は利用者にすれば、アプリケーションが固まってしまったように見えます。

・プログラムはWinSockの利用を終了する時、WSACleanup APIを呼び出して、WinSockがプログラムのために確保した全てのリソースを解放しなければなりません。

・プログラムは WSAStartup APIの呼び出し成功数に応じて、WSACleanup APIを呼び出さなければなりません。最後の WSACleanup API呼び出しのみが実際のリソース解放を行います。それ以外は単に、WS2_32.DLL が持つ内部参照カウント数を減算するだけです。

・重要:sa.sin_addr.s_addr = INADDR_ANY; の設定は外部に無造作に通信窓を開けるため、セキュリティーホールに成り得ます。実際、IPアドレス 204.205.206.207 という得体の知れない(多分偽装?)相手から、接続要求を受け取る羽目に。安易に INADDR_ANY を利用すると、きっと痛い目に合う事に・・・。


【SOCKADDR構造体】

SOCKADDR構造体のメンバー
メンバー名説明
sa_family unsigned short アドレスファミリを表す定数 *1
sa_data char [14] sa_family の固有情報を表すための空き領域

 *1:通常はAF_INET(IPv4インターネットプロトコル)


【SOCKADDR_IN構造体】

SOCKADDR_IN構造体のメンバー
メンバー名説明
sin_family short アドレスファミリを表す定数 *1
sin_port unsigned short ポート番号
sin_addr IN_ADDR IPアドレス
sin_zero char [8] 値0で初期化される空き領域

 *1:通常はAF_INET(IPv4インターネットプロトコル)


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- client の引数がホスト名----------------- お試し結果 -----------------*/

D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2401
From Client : len = 23, data = Let's try Communication

D:\vc2008\dllchk>client localhost
ホスト名でのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication

/*----------------------------------------------------------------------------*/

/*---- client の引数がアドレス----------------- お試し結果 -----------------*/

D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2417
From Client : len = 23, data = Let's try Communication

D:\vc2008\dllchk>client 127.0.0.1
アドレスでのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET s, c;  // s - server socket, c - client socket
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char buf[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((s = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(s, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        return(1);
    }

    // 接続要求待ち
    if(listen(s, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((c = accept(s, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        return(1);
    } else {
        printf("接続要求受付:クライアントアドレス = %s、ポート = %d\n", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
    }

    if(send(c, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        return(1);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        return(1);
    }
    printf("From Client : len = %d, data = %s\n", len, buf);

    // WinSock 切断
    shutdown(s, SD_BOTH);

    // ソケットの破棄
    closesocket(s);
    closesocket(c);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKET c;  // c - client socket
    SOCKADDR_IN sa; // sa - server address
    int i, len;
    char buf[64];

    if(argc != 2) {
        printf("Usage : %s dest\n", argv[0]);
        return(1);
    }

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    if((c = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv[1]);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv[1])) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv[1]);
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            printf("connect1 error : %d\n", WSAGetLastError());
            return(1);
        }
        printf("ホスト名でのサーバ接続完了\n");
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            printf("connect2 error : %d\n", WSAGetLastError());
            return(1);
        }
        printf("アドレスでのサーバ接続完了\n");
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        return(1);
    }
    printf("From Server : len = %d, data = %s\n", len, buf);

    if(send(c, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        return(1);
    }

    // ソケットの破棄
    closesocket(c);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsアプリケーション間で、データの遣り取りをするにはどうすれば良いのか調べてみると、「クリップボード」や「パイプ」、「ソケット通信」、「共有メモリ」など、複数有ることが分かりました。

その中でも今回は、一番(?)取っつき易そうな「ソケット通信」を試してみる事に。

試しに、WinSockAPIでソケット通信の準備と整理を行うだけのサンプルを作ってみました。

printf("IP = %d.%d.%d.%d\n", (BYTE)*(host->h_addr_list[i]), (BYTE)*((host->h_addr_list[i]) + 1), (BYTE)*((host->h_addr_list[i]) + 2), (BYTE)*((host->h_addr_list[i]) + 3));

printf("IP = %s\n", inet_ntoa(*(IN_ADDR *)host->h_addr_list[i]));

上の2つの printf は、どちらも同じ結果を出力します。分かり易い(ピンときた)方を選ぶと良いかも。

・コマンドの引数にローカル指定だけでなく、ヤフーやグーグルなどの外部指定も動作する事を確認しています。


<注意点>

・プログラムは、WSAStartup API呼び出しが成功した後でのみ、他の Windows Sockets 関数を呼び出すことが出来ます。

・WSADATA構造体の wVersion メンバが呼出し元の条件(想定)に合わない場合、プログラムは WSACleanup 関数を呼び出しWinSockDLLリソースを解放して、WinSockAPIの初期化失敗とする事が望ましいようです。

・最新のWinSockの仕様はバージョン2.2です。現在、以下のバージョンをサポートしています。
  1.0、1.1、2.0、2.1、2.2

・WINSOCK_VERSION は、最新バージョンを指定します。


【WSADATA構造体】

WSADATA構造体のメンバー
メンバー名説明
wVersion WORD 呼出し元が想定する WinSock API のバージョン
wHighVersion WORD DLL がサポートする最新の WinSock バージョン
szDescription char [*1] DLL に関する説明
szSystemStatus char [*2] DLL の状態や構成情報
iMaxSockets unsigned short 利用可能な最大ソケット数 *3
iMaxUdpDg unsigned short UDP データグラムの送受信最大サイズ *3
lpVendorInfo char FAR * ベンダー固有の情報へのポインタ

 *1:WSADESCRIPTION_LEN + 1、*2:WSASYS_STATUS_LEN + 1
 *3:WinSock1.1 下位互換のため


【HOSTENT構造体】

HOSTENT構造体のメンバー
メンバー名説明
h_name char FAR * ホストの正式名称
h_aliases char FAR * FAR * ホストの別名のポインタの配列
h_addrype short 要求されたアドレスの種類を表す定数
h_length short アドレスのバイト単位のサイズ *1
h_addr_list char FAR * FAR * ネットワークアドレス情報のポインタの配列

 *1:IPv4は4バイト、IPv6は16バイト


【IN_ADDR構造体】

IN_ADDR構造体のメンバー
メンバー名説明
s_addrunsigned longアドレスを表す数値 *1

 *1:ドット表記 xxx.yyy.zzz.xyz のIPアドレスを数値形式に変換した値


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- WinSockAPI ソケット使用 -------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---------------------------------- お試し結果 ----------------------------*/

D:\vc2008\dllchk>test
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running

D:\vc2008\dllchk>test localhost
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running
正式名称 = biroku
IP = 127.0.0.1

D:\vc2008\dllchk>test 127.0.0.1
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running
正式名称 = localhost
IP = 127.0.0.1

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    LPHOSTENT host;
    IN_ADDR addr;
    int i, ret;

    // WinSock の初期化
    if((ret = WSAStartup(WINSOCK_VERSION, &wsaData)) != 0) {
        printf("WinSockの初期化に失敗しました:error = %d\n", ret);
        return(0);
    }
    printf("最新バージョン = %d.%d\nDLLの説明 = %s\n現在の状態 = %s\n", (BYTE)wsaData.wHighVersion, wsaData.wHighVersion >> 8, wsaData.szDescription, wsaData.szSystemStatus);

    if((BYTE)wsaData.wHighVersion < 2) {
        printf("WinSockの想定バージョンとサポートバージョンに相違があります。\n");
        return(0);
    }

    if(argc == 2) {
        addr.s_addr = inet_addr(argv[1]);
        if(addr.s_addr == INADDR_NONE) { // ホスト名なら
            host = gethostbyname(argv[1]);
        } else { // アドレスなら
            host = gethostbyaddr((const char *)&addr, sizeof(IN_ADDR), AF_INET);
        }
        if(host == NULL) {
            printf("ホスト名の取得に失敗しました:%s\n", argv[1]);
            return(0);
        }

        printf("正式名称 = %s\n", host->h_name);
        for(i = 0; host->h_aliases[i] != NULL; i++) {
            printf("別名 = %s\n", host->h_aliases[i]);
        }

        for(i = 0; host->h_addr_list[i] != NULL; i++) {
//          printf("IP = %d.%d.%d.%d\n", (BYTE)*(host->h_addr_list[i]), (BYTE)*((host->h_addr_list[i]) + 1), (BYTE)*((host->h_addr_list[i]) + 2), (BYTE)*((host->h_addr_list[i]) + 3));
            printf("IP = %s\n", inet_ntoa(*(IN_ADDR *)host->h_addr_list[i]));
        }
    }

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/