ソケットプログラミング

最近読んでるTCP/IPの本asin:4274065847の内容を試してみたいのと,

windowsでソフト作ってみたいのと,

二つの欲求からまずは簡単なものを作った.


サーバが5001/tcpで待ちうけて,クライアントとコネクション張れたら切断する,

というシンプルなもの.


文字を送るとかはまだ未実装.


サーバ側.

#include <stdio.h>
//#include <WinSock.h> /* library: Wsock32.lib */
#include <winsock2.h>  /* library: ws2_32.lib/上位互換性があるのでこっちを使う */
#include <stdlib.h>

/* DEFAULT_PORT
 * 「Windows系OSのデフォルト設定ではエフェメラルポートは1024-5000」
 * http://www.windows-world.jp/special/-/126591-3.html
 */
#define DEFAULT_PORT	5001
/* NBACKLOG
 * 保留中の接続のキューの最大値
 * キューが小さいと,クライアントからの接続を拒否することになる
 * http://www.linux.or.jp/JM/html/LDP_man-pages/man2/listen.2.html
 */
#define NBACKLOG	5


/* 
 * サーバ側の通信の手順
 * socket() -> bind() -> listen() -> accept() -> closesocket()
 */


int 
main()
{
	WSADATA wsaData;  /* WinSockのバージョン情報を格納
                           * http://www.geocities.jp/playtown1056/program/ws_1.htm
                           */
	SOCKET sock;
	struct sockaddr_in addr;     /* サーバのIPアドレスとポート番号を格納 */
	int addr_len;                /* addrのサイズ;accept()で使用 */
	u_short port = DEFAULT_PORT; /* 自ホストの待ちうけポート */
	int backlog = NBACKLOG;      /* NBACKLOG(上記)を参照 */


	/* WinSock2のDLL(ws2_32.lib)の初期化 */
	WSAStartup(MAKEWORD(2, 0), &wsaData);
                    /* MAKEWORD(a,b):要求するバージョンを
                     *               記述するマクロ
                     * a: メジャーバージョン
                     * b: マイナーバージョン
                     * e.g.) MAKEWORD(2.2): WinSock2.2を要求
                     */

	/* ネットワークソケットの作成 */
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
		fprintf(stderr, "failed to socket()\n");
		return EXIT_FAILURE;
	}

	/* 自分のアドレスの構造体を設定 */
	memset(&addr, 0x0, sizeof(sockaddr_in));
	addr.sin_family = AF_INET;      /* UDP, TCP, etc. (WinSock.h) */
	addr.sin_port = htons(port);    /* ネットワークバイトに変換 */
	// addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addr.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK);
                       /* 自ホストのIPなのだからINADDR_LOOPBACKではダメなの?
                        * http://www.linux.or.jp/JM/html/LDP_man-pages/man7/ip.7.html
                        * http://eternalwindows.jp/network/winsock/winsock03s.html
                        * ⇒ループバックでもできた.
                        */

	/* ソケットとsockaddr(IPアドレスとポート)の関連付け */
	if (bind(sock, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
		fprintf(stderr, "failed to bind()\n");
		return EXIT_FAILURE;
	}

	/* コネクション受付開始 */
	if (listen(sock, backlog) == SOCKET_ERROR) {
		fprintf(stderr, "failed to listen()\n");
		return EXIT_FAILURE;
	}

	/* コネクション受理 */
	addr_len = sizeof(addr);
	if (accept(sock, (sockaddr *)&addr, &addr_len) == INVALID_SOCKET) {
		fprintf(stderr, "failed to accept()\n");
		return EXIT_FAILURE;
	}

	/* コネクションを閉じる */
	if (closesocket(sock) == SOCKET_ERROR) {
		fprintf(stderr, "failed to closesocket()\n");
		return EXIT_FAILURE;
	}

	/* WinSockの終了処理 */
	WSACleanup();

	return EXIT_SUCCESS;
}

/* vim: set ts=4: */


クライアント側.

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

/* DEFAULT_PORT
 * サーバ側で待ちうけポートを5001に決めたので,この番号.
 */
#define DEFAULT_PORT	5001
#define NBACKLOG	5

/* コマンドライン引数の順番 */
enum {
	CMD_NAME = 0,       /* コマンドネーム */
	DST_IP,             /* サーバのIP */
	// DST_PORT,        /* ポートが指定できるようにしてもいいかもしれない */
};


/*
 * クライアント側の通信の手順
 * socket() -> connect() -> closesocket()
 */


int 
main(int argc, char *argv[])
{
	WSADATA wsaData;
	SOCKET sock;
	struct sockaddr_in addr;     /* サーバのIPアドレスとポート番号を格納 */
	unsigned long dst_ip;        /* サーバのIPアドレス(一時変数) */
	u_short port = DEFAULT_PORT; /* サーバの待ちうけポート */


	WSAStartup(MAKEWORD(2, 0), &wsaData);

	/* ドット表記のIPアドレスをバイナリに変換する */
	if ((dst_ip = inet_addr(argv[DST_IP])) == INADDR_NONE) {
		fprintf(stderr, "invalid address: %s\n", argv[DST_IP]);
		return EXIT_FAILURE;
	}

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
		fprintf(stderr, "failed to socket()\n");
		return EXIT_FAILURE;
	}

	/* サーバのアドレスの構造体を設定 */
	memset(&addr, 0x0, sizeof(sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.S_un.S_addr = dst_ip;

	/* サーバに接続要求 */
	if (connect(sock, (sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
		fprintf(stderr, "failed to bind()\n");
		return EXIT_FAILURE;
	}

	/* コネクションを閉じる */
	if (closesocket(sock) == SOCKET_ERROR) {
		fprintf(stderr, "failed to closesocket()\n");
		return EXIT_FAILURE;
	}

	/* WinSockの終了処理 */
	WSACleanup();

	return EXIT_SUCCESS;
}

/* vim: set ts=4: */