ソケットプログラミング

コンピュータどうしで通信する際、ソケットを利用します。ソケットとは、電球を入れるいれものですね。あの電球とソケットのようにつなげるイメージをこめて、コンピュータどうしをつなげるのにソケットと命名したのでしょう。 さて、このソケットは「Windows、Mac」といったOSがもっているAPIを使用することで利用可能です。プログラマはこのソケットをプログラムに取り組めばコンピュータどうしの通信が可能となります。 ここでは、MFCを使ってソケットプログラミングを行います。具体的には、GoogleとソケットをつなげてHTMLファイルを読み込むプログラムを作ります。

WinSockを使えるようにする

WindowsではWinSockというソケットプログラミング用APIが用意されているのでこれを使います。

WinSockを使うには「WSock32.lib」をリンクする必要があります。

プロジェクトのプロパティのリンカーの入力にある追加の依存ファイルに「WSock32.lib」としてつなげましょう。 あとは

WinSock2.hをインクルードしておきます。

これで命令を使用できるようになります。

手順

プログラミングの手順は以下のようになります。

WinSockの初期化(WSAStartup関数)

WSAStartup関数でWinSockを初期化します。WSAStartup関数は以下のようになっています。 int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

  • wVersionRequested:Winsockのバージョンを指定
  • lpWSAData:WSADATA構造体へのポインタ

wVersionRequestedにはMAKEWORDマクロを使ってWinsockのバージョンを指定します。 WORD MAKEWORD(BYTE bLow,BYTE bHigh); lpWSADataにはWSADATA構造体と呼ばれるWindowsソケットに関する情報格納変数のポインタを指定します。

ソケットを作成する(socket関数)

WinSockの初期設定がすんだらソケットを準備します。ソケットの準備にはsocket関数を使います。 SOCKET socket(int af,int type,int protocol);

  • af:アドレスファミリ、ここではインターネットなのでAF_INETを指定します。
  • type:TCPを使うので、SOCK_STREAMを指定します。
  • protocol:プロトコール、AF_INETのときは0を記入します。

ソケットをつなげる(connect関数)

ソケットを作成したら、サーバーとつなげます。その際にconnect関数を使用します。 int connect(SOCKET s, const struct sockaddr FAR *name, int namelen);

  • s: ソケットの指定
  • name: サーバー情報を設定したSOCKADDR_IN構造体を指定
  • namelen: nameのサイズ

SOCKADDR_IN構造体にサーバー情報を指定して、つなげたいサーバーを教えます。

  1. sin_family: アドレスファミリ
  2. sin_port:ポート番号
  3. sin_addr:IPアドレス

メッセージをおくる(send関数)

サーバーに接続したら、メッセージをサーバーに送ります。その際にsend関数を使います。 int send(SOCKET s, const char FAR *buf,int len,int flags);

  • s:ソケット
  • buf:送信したい文字列
  • len:bufのサイズ
  • flags:0を指定

メッセージを受け取る(recv関数)

サーバーからメッセージを受け取ります。関数はrecvを使用します。 int recv(SOCKET s, char FAR *buf, int len, int flags);

  • s:ソケット
  • buf:受信したデータが格納される
  • len:bufのサイズ

終了処理

最後にソケットを閉じたりWinSockの終了処理をしておわりです。

プログラム

では、以上の内容をプログラミングしていきます。以下にソースをのせます。

void CNetWorkDlg::OnBnClickedOk()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	WSADATA wsaData;
	LPHOSTENT lpHost;
	SOCKET s;
	int nRtn;
	SOCKADDR_IN sockadd;
	char szServer[256],szURL[256],szStrRcv[1024];
	char szStr[256];
	u_short port=80;	//ポート番号
	//Webサーバー名 szServerとURLを入力
	strcpy_s(szServer,CStringA("www.google.com"));
	strcpy_s(szURL,CStringA("http://www.google.com/"));

	//WinSockの初期化****************************
	if(WSAStartup(MAKEWORD(1,1),&wsaData)!=0){
		AfxMessageBox("WSAStartupエラー");
		return;
	}

	//ソケットを開く*****************************
	s=socket(AF_INET,SOCK_STREAM,0);
		//AF_INET->アドレスファミリはインターネット,
		//SOCK_STREAM->TCPを使う
		//0->プロトコール:AF_INETなら0
	if(s==INVALID_SOCKET){
		AfxMessageBox("ソケットを開けません");
		WSACleanup();
		return;
	}
	lpHost=gethostbyname(szServer);
	if(lpHost==NULL){
		AfxMessageBox("gethostbynameエラー");
		WSACleanup();
		return;
	}

	//メモリの確保とsockaddの設定****************
	memset(&sockadd,0,sizeof(sockadd));
	sockadd.sin_family=AF_INET;	//アドレスファミリ(インターネットだからAF_INET)
	sockadd.sin_port=htons(port);	//ポート番号
	sockadd.sin_addr=*((LPIN_ADDR)*lpHost->h_addr_list);	//サーバーアドレス

	//サーバーに接続*****************************
	if(connect(s,(PSOCKADDR)&sockadd,sizeof(sockadd))!=0){
		closesocket(s);
		WSACleanup();
		return;
	}

	//GET命令をサーバーに送る********************
	wsprintf(szStr,"GET %s HTTP/1.0\r\n\r\n",szURL);
	nRtn=send(s,szStr,(int)strlen(szStr),0);

	while(1){
		//メモリを確保しメッセージを受け取る*****
		memset(szStrRcv,'\0',sizeof(szStrRcv));
		nRtn=recv(s,szStrRcv,(int)sizeof(szStrRcv)-1,0);
		if(nRtn==0){
			break;
		}
		if(nRtn==SOCKET_ERROR){
			AfxMessageBox("recvエラー");
			break;
		}
		AfxMessageBox(szStrRcv);	//表示
	}

	closesocket(s);
	WSACleanup();
	return;
}

著者:安井 真人(やすい まさと)