Win32/64ネットワーク開発 004 winsockを使ったhttp通信

提供:yonewiki
2023年12月4日 (月) 19:35時点におけるYo-net (トーク | 投稿記録)による版 (→‎概要)

Win32/64 ネットワーク処理開発へ戻る。

前の記事:Win32/64ネットワーク開発 003 wininetを使ったftp通信

次の記事:[[Win32/64ネットワーク開発 005]]

概要

 WinSockという通信の窓口を使って、ネットワークを通信してみたいと思います。自分ではネットワークの基礎となる装置を作るのは大変なのでWindowsではコレを使うのが最も基礎的なネットワークのプログラムを始める原点になると思います。


 まずはhttp通信を処理するプログラムを作りますが、今はhttpsという通信が主要なので、あまり役に立たないですね。でも、https通信を行うとなると一気に難しくなります。httpsはSSL(Secure Socket Layer)やTLS(Transport Layer Security)という暗号方式で通信を行います。暗号通信おそるべし。そしてftpにもSSLがあります。ftpsですね。リモートにあるサーバを端末から操作する際、通信される一切の情報を暗号化するSSH(Secure SHell)を使ったSFTPもあるか。UNIX系のシェルが備えられている場合に使われるSCP(Secure Copy Protocol)もあるね。いろいろあります。ネットワーク通信楽しいけど、通信の道を出歩くときはちゃんと装備をして出歩かないと、街で強盗に合うような、通信の傍受をされることもあります。悪いことをする人がいなくなるといいんだけど、不平不満がなくならない世界に悪はなくならない。我慢が足りない人はたくさん存在するし、裕福な人の生活があり、そこをうらやむ人がいて、埋めようとする人がいる。ソフトウェアの勉強でもして人生を消費するのも楽しいのにね。Windows/Linux/Macパソコンがあるだけで裕福だと思うし、ましてや電力契約・ネットワーク通信契約が存在する環境があるなんて裕福過ぎる。スマホだと開発はあまりできないから、SNSに時間を消費したり暇になっちゃうかな。ここの管理人はSNSはやらないっすけどね。使い方だけちょろっと知って、あとはそっとしておくっす。


 なんにしても、複雑なことをする前に、風前の灯となりつつあるHTTP通信の習得からですね。


#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <Windows.h>
#include <cstdio>
#include <winsock2.h>
#include <iostream>
#include <string>
#pragma comment( lib, "ws2_32.lib" )
int main()
{
	WSADATA WSADATAwsaData;
	LPHOSTENT LPHOSTENTlpHostnet;
	SOCKET SOCKETs;
	int iReturn;
	SOCKADDR_IN SOCKADDR_INsockadd;
	char szServer[256], szURL[256], szStrRcv[1024], szPort[8], szYesNo[4];
	wchar_t wchStr[256];
	u_short u_shortPort;
	unsigned int uiAddr;

	while (1) {
		if (WSAStartup(MAKEWORD(1, 1), &WSADATAwsaData) != 0) {
			perror("WSAStartupエラー\n");
			return -1;
		}

		printf("Webサーバー名(ex.www.yo-net.jp):");
		std::cin >> szServer;
		printf("ポート番号(ex.80):");
		std::cin >> szPort;
		if (strcmp(szPort, "") == 0) {
			strcpy_s(szPort, strlen("80"), "80");
		}

		u_shortPort = (u_short)atoi(szPort);
		printf("URL(ex.http://www.yo-net.jp/index.html):");
		std::cin >> szURL;
		if (strcmp(szURL, "") == 0){
			strcpy_s(szURL, strlen("/"), "/");
		}

		//ソケット処理
		SOCKETs = socket(PF_INET, SOCK_STREAM, 0);
		if (SOCKETs == INVALID_SOCKET) {
			perror("ソケットをオープンできない。\n");
			WSACleanup();
			return -2;
		}

		//ホスト情報取得処理
		LPHOSTENTlpHostnet = gethostbyname(szServer);
		if (LPHOSTENTlpHostnet == NULL) {
			uiAddr = inet_addr(szServer);
			LPHOSTENTlpHostnet = gethostbyaddr((char*)&uiAddr, 4, AF_INET);
			if (LPHOSTENTlpHostnet == NULL) {
				int iUTF16Len = MultiByteToWideChar(932, 0, szServer, -1, NULL, 0);
				std::wstring wstringCP932Str(iUTF16Len, 0);
				if (iUTF16Len > 0) {
					MultiByteToWideChar(932, 0, szServer, -1, &wstringCP932Str[0], iUTF16Len);
				}

				wsprintf(wchStr, L"%sが見つからない\n", wstringCP932Str.c_str());
				int iCP932Len = WideCharToMultiByte(932, 0, wchStr, -1, NULL, 0, NULL, NULL);
				std::string stringCP932Str(iCP932Len, 0);
				if (iCP932Len > 0) {
					WideCharToMultiByte(932, 0, wchStr, -1, &stringCP932Str[0], iCP932Len, NULL, NULL);
				}
				perror(stringCP932Str.c_str());
				WSACleanup();
				return -3;
			}
		}

		//SOCKADDR型構造体への値設定処理
		memset(&SOCKADDR_INsockadd, 0, sizeof(SOCKADDR_INsockadd));
		SOCKADDR_INsockadd.sin_family = AF_INET;
		SOCKADDR_INsockadd.sin_port = htons(u_shortPort);
		SOCKADDR_INsockadd.sin_addr = *((LPIN_ADDR)*LPHOSTENTlpHostnet->h_addr_list);
		
		//接続処理
		if (connect(SOCKETs, (PSOCKADDR)&SOCKADDR_INsockadd, sizeof(SOCKADDR_INsockadd)) != 0) {
			perror("サーバーソケットに接続失敗\n");
			closesocket(SOCKETs);
			WSACleanup();
			return -4;
		}

		//送信メッセージ形成処理
		int iUTF16Len = MultiByteToWideChar(932, 0, szURL, -1, NULL, 0);
		std::wstring wstringCP932Str(iUTF16Len, 0);
		if (iUTF16Len > 0) {
			MultiByteToWideChar(932, 0, szURL, -1, &wstringCP932Str[0], iUTF16Len);
		}

		wsprintf(wchStr, L"GET %s HTTP/1.0\r\n\r\n", wstringCP932Str.c_str());
		int iCP932Len = WideCharToMultiByte(932, 0, wchStr, -1, NULL, 0, NULL, NULL);
		std::string stringCP932Str(iCP932Len, 0);
		if (iCP932Len > 0) {
			WideCharToMultiByte(932, 0, wchStr, -1, &stringCP932Str[0], iCP932Len, NULL, NULL);
		}

		//送信処理
		iReturn = send(SOCKETs, stringCP932Str.c_str(), (int)strlen(stringCP932Str.c_str()), 0);

		//受信処理 受信最終文字までループ継続
		while (1) {
			memset(szStrRcv, '\0', sizeof(szStrRcv));
			iReturn = recv(SOCKETs, szStrRcv, (int)sizeof(szStrRcv) - 1, 0);
			printf("%s", szStrRcv);
			if (iReturn == 0){
				break;
			}
			if (iReturn == SOCKET_ERROR) {
				perror("recvエラー\n");
				break;
			}
		}

		//終了処理 シャットダウン→クローズ→WSAクリア
		if (shutdown(SOCKETs, SD_BOTH) != 0) {
			perror("ソケットシャットダウン失敗\n");
		}

		closesocket(SOCKETs);
		WSACleanup();
		
		//プログラム継続判定
		printf("\n再実行(Y/N):");
		std::cin >> szYesNo;
		if (strcmp(szYesNo, "n") == 0 || strcmp(szYesNo, "N") == 0){
			break;
		}
	}
	return 0;
}


 上記のコードはコマンドプロンプトで動くコンソールアプリケーションです。プロジェクトのプロパティでリンカーの入力の追加の依存ファイルに;WSock32.lib;ws2_32.libを追加しなければなりません。


 では少しづつ、動きを確認してみましょう。


 1行目の#define WIN32_LEAN_AND_MEANがある場合Windows.hから以下のインクルードが除外されます。

  • #include <cderr.h>
  • #include <dde.h>
  • #include <ddeml.h>
  • #include <dlgs.h>
  • #include <lzexpand.h>
  • #include <mmsystem.h>
  • #include <nb30.h>
  • #include <rpc.h>
  • #include <shellapi.h>
  • #include <winperf.h>
  • #include <winsock.h>
  • #include <wincrypt.h>
  • #include <winefs.h>
  • #include <winscard.h>
  • #include <winspool.h>
  • #include <ole2.h>
  • #include <commdlg.h>


 

前の記事:Win32/64ネットワーク開発 003 wininetを使ったftp通信

次の記事:[[Win32/64ネットワーク開発 005]]

Win32/64 ネットワーク処理開発へ戻る。