非同期接続型ソケット通信プログラミング

眠すぎるけど、気合で少しは勉強しよう。

名前はいかめしいが、ようするに、チャットを作りたいんですね。インターネットでする分には、そういうネットワーク周りは、全部httpサーバがやってくれますが、C++で作ろうと思ったら、そういう低階層のところまでこちらで制御してやらんので、大変。

これでも、TCP/IP等の知識はなくても作ってくれるので、簡単らしい。
なんというか、ハードウェアエンジニアってすげーと思います。

とりあえず、備忘録

/** コントロールを使える/使えなくする.
 *@param HWND リソースのコントロールID
 *@param BOOL   FALSEでつかなくなる。 
 */
EnableWindow( HWND hSendBtn, BOOL FALSE);

猫でもわかるネットワークプログラミングを参考に、ひたすら打ち続けた。
本当に誤字が気になる。セミコロンの忘れもけしからんが、引数の数間違いや、カンマの打ち忘れは許せません。

明日学校に残れたらクライアント側も作る予定なので、ここに今日書いたコードを載せておこう。完全にBlogが備忘録扱い.

// wserver.cpp : アプリケーション用のエントリ ポイントの定義
//

#define WIN32_LEAN_AND_MEAN

#include "stdafx.h"
#include 
#include 
#include 
#include "resource.h"
BOOL InitInstance();
LRESULT CALLBACK ViewW( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int mystream( HWND hDlg, u_short uport );
SOCKET s;
const char ClassName[]="Hatena_Sample";
HWND D1;
HINSTANCE hInst;
ATOM			MyRegisterClass();
BOOL bSOCKET_S = FALSE, bSOCKET_LISTEN = FALSE;;
#define MY_MSG ( WM_USER + 1)

SOCKET listen_s;


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
	MSG msg;
 	// TODO: この位置にコードを記述してください。
	WSADATA wsaData;
	if ( WSAStartup( MAKEWORD(1,1), &wsaData ) ) {
		MessageBox(NULL, "初期化失敗", "Error", MB_OK | MB_ICONEXCLAMATION );
		WSACleanup();
		return -1;
	}



	hInst = hInstance;//GetModuleHandle(NULL);
    if(!MyRegisterClass()){
		return 0;
	}
	// アプリケーションの初期化を行います:
	if( !InitInstance() ) 
	{
		ExitProcess(0);
        return 0;
	}
	while( GetMessage(&msg, NULL, 0, 0) ) 
	{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
	}
    ExitProcess(msg.wParam);

	if ( WSACleanup() != 0 ) {
		MessageBox(NULL, "WSACleanupエラーです", "Error", MB_OK );
	}

	MessageBox(NULL, "終了します", "終了", MB_OK );

  	return 0;
}
BOOL InitInstance()
{
   HWND hWnd;
   hWnd = CreateWindow(ClassName,"", WS_OVERLAPPEDWINDOW,
          0, 0, 0, 0, NULL, NULL, hInst, NULL);
//    ShowWindow( hWnd, 1 ); メインWindowは表示しない
   if( !hWnd ) 
   {
      return FALSE;
   }
    DialogBox(hInst, (LPCTSTR)IDD_FORMVIEW, hWnd, (DLGPROC)ViewW);
	return TRUE;
}
LRESULT CALLBACK ViewW( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
	static HWND hStart, hInfo, hSendBtn;
	static u_short port;
	BOOL bSuccess;
	int nRet, nEvent;
	char szBuf[1024];
	static char szAllBuf[1024 * 10 ] = "";
	SOCKADDR from;
	int fromlen, nError, nAsync;
	switch( message )
	{
		case WM_INITDIALOG:
//タスクバーに表示されるようにする
          ::SetWindowLong(hDlg, GWL_EXSTYLE,
          ::GetWindowLong(hDlg, GWL_EXSTYLE) | WS_EX_APPWINDOW);
//アイコンを登録してみる
		  SendMessage (hDlg, WM_SETICON, ICON_BIG, (LPARAM)(HICON) LoadIcon(hInst, (LPCTSTR)IDI_ICON1));
			SendMessage (hDlg, WM_SETICON, ICON_SMALL, (LPARAM)(HICON) LoadIcon(hInst, (LPCTSTR)IDI_ICON1));
			D1=hDlg;
			hStart = GetDlgItem( hDlg, IDC_START);
			hInfo  = GetDlgItem( hDlg, IDC_INFO );
			hSendBtn = GetDlgItem( hDlg, IDC_SENDBTN );
			EnableWindow( hSendBtn, FALSE );
			return TRUE;
		case WM_COMMAND:
			if( LOWORD(wParam) == IDC_BUTTON1 ) 
			{
//				MessageBox(NULL, "終了が押下", "IDC_START", MB_OK );
				if ( bSOCKET_S ) {
					nRet = shutdown( s, SD_BOTH );
					if ( nRet == SOCKET_ERROR ) {
						MessageBox( hDlg, "シャットダウン失敗", "Error", MB_OK );
					} else {
						MessageBox( hDlg, "シャットダウン成功", "shutdown", MB_OK );
					}

					if ( closesocket( s ) ) {
						MessageBox( hDlg, "ソケットクローズ失敗", "Error", MB_ICONEXCLAMATION | MB_OK );
					}
				}
				if ( bSOCKET_LISTEN ) {
					if ( closesocket( listen_s ) ) {
						MessageBox( hDlg, "リスンソケットクローズ失敗", "Error", MB_ICONEXCLAMATION | MB_OK );
					}
				}
	
				EndDialog(hDlg, LOWORD(wParam));
				PostQuitMessage( 0 );
			    return TRUE;
			}
			if ( LOWORD( wParam ) == IDC_START ) {
			//	MessageBox(NULL, "IDC_STARTが押下", "IDC_START", MB_OK );
				port = (u_short)GetDlgItemInt(hDlg, IDC_PORT, &bSuccess, FALSE);
				if ( mystream( hDlg, port ) == 0 ) {
					EnableWindow( hStart, FALSE );
				}
				return TRUE;
			}
			if ( LOWORD( wParam ) == IDC_SENDBTN ) {
				//MessageBox(NULL, "IDC_SENDBTNが押下", "IDC_START", MB_OK );
				GetDlgItemText( hDlg, IDC_SEND, szBuf, (int)sizeof( szBuf ) );
				nRet = send( s, szBuf, (int)strlen(szBuf), 0 );
				strcat( szAllBuf, "Server:");
				strcat(szAllBuf, szBuf);
				strcat(szAllBuf, "\r\n");
				SetDlgItemText(hDlg, IDC_INFO, szAllBuf );
				SendMessage( hInfo, EM_LINESCROLL,
					0,
					Edit_GetLineCount( hInfo ));
				SetDlgItemText( hDlg, IDC_SEND, "");
				SetFocus( GetDlgItem( hDlg, IDC_SEND ));
				return TRUE;
			}
			if ( LOWORD( wParam ) == MY_MSG ) {
				nEvent = WSAGETSELECTEVENT( lParam );
				switch ( nEvent ) {
				case FD_CLOSE:
					MessageBox(hDlg, "クライアントが切断してきました\n", "再待ち受けします", MB_OK );
					EnableWindow( hSendBtn, FALSE );
					closesocket( s );
					bSOCKET_S = FALSE;
					mystream( hDlg, port );
					return TRUE;
				case FD_READ:
					nRet = recv( s, szBuf, (int)sizeof(szBuf) -1 , 0 );
					if ( nRet == SOCKET_ERROR  ) {
						MessageBox( hDlg, "recvエラー", "error", MB_OK );
						return FALSE;
					}
					szBuf[nRet] = '\0';
					strcat( szAllBuf, "Client:");
					strcat( szAllBuf, szBuf );
					strcat( szAllBuf, "\r\n");
					SetDlgItemText( hDlg, IDC_INFO, szAllBuf );
					SendMessage( hInfo, EM_LINESCROLL, 0, Edit_GetLineCount( hInfo ));
					return TRUE;
				case FD_ACCEPT:
					memset( &from, 0, sizeof( SOCKADDR ));
					fromlen = sizeof( from );
					s = accept( listen_s, (SOCKADDR *)&from, &fromlen );
					if ( s == INVALID_SOCKET ) {
						nError = WSAGetLastError();
						if ( nError != WSAEWOULDBLOCK ) {
							switch( nError ) {
							case WSANOTINITIALISED:
								MessageBox( hDlg, "WSANOTINITIALISED", "Error", MB_OK );
								break;
							case WSAENETDOWN:
								MessageBox( hDlg, "WSAENETDOWN", "Error", MB_OK );
								break;
							case WSAEFAULT:
								MessageBox( hDlg, "WSAEFAULT", "Error", MB_OK );
								break;
							case WSAEINTR:
								MessageBox( hDlg, "WSAEINTR", "Error", MB_OK );
								break;
							case WSAEINPROGRESS:
								MessageBox( hDlg, "WSAEINPROGRESS", "Error", MB_OK );
								break;
							case WSAEINVAL:
								MessageBox( hDlg, "WSAEINVAL", "Error", MB_OK );
								break;
							case WSAEMFILE:
								MessageBox( hDlg, "WSAEMFILE", "Error", MB_OK );
								break;
							case WSAENOBUFS:
								MessageBox( hDlg, "WSAENOBUFS", "Error", MB_OK );
								break;
							case WSAENOTSOCK:
								MessageBox( hDlg, "WSAENOTSOCK", "Error", MB_OK );
								break;
							case WSAEOPNOTSUPP:
								MessageBox( hDlg, "WSAEOPNOTSUPP", "Error", MB_OK );
								break;
							default:
								MessageBox( hDlg, "不明のエラー", "Error", MB_OK );
								break;
							}
							MessageBox( hDlg, "accept error", "Error", MB_OK );
							closesocket( listen_s );
							bSOCKET_LISTEN = FALSE;

							return TRUE;
						}
					}
					if ( closesocket( listen_s ) != 0 ) {
						MessageBox( hDlg, "Error", "Error", MB_OK );
						return TRUE;
					}
					bSOCKET_LISTEN = FALSE;
					bSOCKET_S = TRUE;
					nAsync = WSAAsyncSelect( s, hDlg, MY_MSG, FD_CLOSE | FD_READ );
					if ( nAsync != 0 ) {
						MessageBox( hDlg, "非同期化失敗", "Error", MB_OK | MB_ICONEXCLAMATION );
						closesocket( s );
						bSOCKET_S = FALSE;
						return TRUE;
					}
					EnableWindow( hSendBtn, TRUE );
					return TRUE;
				}
				return FALSE;
			}
			break;
	}
    return FALSE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch( message ) 
	{
		case WM_DESTROY:
			PostQuitMessage( 0 );
			break;
		default:
			return DefWindowProc( hWnd, message, wParam, lParam );
    }
	return 0;
}

ATOM MyRegisterClass()
{
	WNDCLASS wcex;
	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInst;
    wcex.hIcon		    = LoadIcon(hInst, (LPCTSTR)IDI_ICON1);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= ClassName;
	return RegisterClass( &wcex );
}

#define MY_MSG ( WM_USER + 1)

int mystream( HWND hDlg, u_short uport ) {
	SOCKADDR_IN saddr;
	int nAsync;
	
	listen_s = socket( AF_INET, SOCK_STREAM, 0 ); //listen_s はSOCKET
	if ( listen_s < 0 ) {
		if ( WSAGetLastError() != WSAEWOULDBLOCK ) {
			MessageBox( hDlg, "ソケットオープンエラー", "Error", MB_OK | MB_ICONEXCLAMATION );
			return -1;
		}
	} else {
		MessageBox( NULL, "ソケットオープンしました", "socket成功", MB_OK );
	}
	bSOCKET_LISTEN = TRUE;	// bSOCKET_LISTEN はBOOL

	nAsync = WSAAsyncSelect( listen_s, hDlg, MY_MSG, FD_ACCEPT );
	if ( nAsync != 0 ) {
		MessageBox( hDlg, "非同期化失敗1", "Error", MB_OK | MB_ICONEXCLAMATION );
		closesocket( listen_s );
		bSOCKET_LISTEN = FALSE;
		return -2;
	}

	memset( &saddr, 0, sizeof( SOCKADDR_IN ));
	saddr.sin_family	= AF_INET;
	saddr.sin_port		= htons(uport);
	saddr.sin_addr.s_addr = INADDR_ANY;

	if ( bind( listen_s, (SOCKADDR *)&saddr, sizeof(saddr)) == SOCKET_ERROR ) {
		MessageBox( hDlg, "bind Error\n", "別のポート番号でもう一度試してみてください。", MB_OK );
		closesocket( listen_s );
		bSOCKET_LISTEN = FALSE;
		return -3;
	}

	if ( listen( listen_s, 0 ) == SOCKET_ERROR ) {
		if ( WSAGetLastError() != WSAEWOULDBLOCK ) {
			MessageBox( hDlg, "listen Error", "Error", MB_OK );
			closesocket( listen_s );
			bSOCKET_LISTEN = FALSE;
			return -4;
		}
	} else {
		MessageBox( hDlg, "クライアントの接続待ちです", "OK", MB_OK );
	}

	return 0;
}