윈도우 네트워크 프로그래밍

비동기 모델 (2010년 4월 15일)

지화명이 2010. 4. 15. 11:40


// Winsoc02.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <winsock2.h> //windows.h보다 먼저 선언해야함
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <iostream>

 

#define BUFSIZE 512

unsigned int WINAPI ServerProc(LPVOID lpParam);
unsigned int WINAPI ClientContorl(LPVOID lpParam);

 

int _tmain(int argc, _TCHAR* argv[])
{
 DWORD dwID;

 HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, (unsigned *)&dwID); //스레드 생성

 WaitForSingleObject(h,INFINITE);  //스레드 종료를 기다림

 printf("Main Thread 종료\n");

 CloseHandle(h);           //핸들을 반환

 return 0;
}

// 링커->입력->추가종속성에 ws2_32.lib를 추가 해주어야 한다.

unsigned int WINAPI ServerProc(LPVOID lpParam)
{
 WSADATA wsa;
 int retval;
 //윈속 초기화
 if(WSAStartup(MAKEWORD(2,2),&wsa) !=0)
  return -1;

 SOCKET listen_sock = socket(AF_INET,SOCK_STREAM,0);

 if(listen_sock == INVALID_SOCKET)
  printf("소켓 초기화 실패\n");

 //---------------넌 블록킹 소켓으로 전환--------
 u_long on =1;
 retval = ioctlsocket(listen_sock,FIONBIO,&on);
 if(retval==SOCKET_ERROR)
 {
  printf("소켓 속성 변경 실패\n");
  return 0;
 }
 //----------------------------------------------

 SOCKADDR_IN serveraddr;

 ZeroMemory(&serveraddr,sizeof(serveraddr));
 serveraddr.sin_family= AF_INET;
 serveraddr.sin_port=htons(9000);
 serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);

 //bind 서버의 지역 IP주소와 지역 포트번호를 결정
 //(클라이언트의 접속을 수용할 소켓,이변수늬 주소와 지역포트 번호로 초기화 시킴,소켓주소 구조체변수의 길이)
 retval=bind(listen_sock,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
 if(retval==SOCKET_ERROR)
  printf("bind error\n");

 //listen
 //client의 접속을 받아들일수 있는 상태로 포트 상태를 변경한다.
 retval=listen(listen_sock,SOMAXCONN);
 if(retval==SOCKET_ERROR)
  printf("listen error\n");


 SOCKET client_sock;
 SOCKADDR_IN clientaddr;
 int addrlen;
 HANDLE hThread;
 DWORD THreadID;

 //accept()
 addrlen = sizeof(clientaddr);
 //클라이언트에서 접근을 하면 연결을 허락해준다.
 //(소켓 서버, 클라이언트의 주소(sockaddr 타입), 클라이언트 주소의사이즈)

 do
 {
  printf("client 접속대기\n");
  client_sock = accept(listen_sock,(SOCKADDR *)&clientaddr,&addrlen);
  if(client_sock ==INVALID_SOCKET)
  { //WSAEWOULDBLOCK이란 읽을려고 하는데 첫번째 데이터가 없을때 발생이 된다.
   //즉 NonBlocking 모드에서는 자주 발생할수 밖에 없는 에러이다.
   if(WSAGetLastError()!=WSAEWOULDBLOCK)
    printf("accept error\n");
  }
  Sleep(500);
 }while(client_sock==INVALID_SOCKET);

 printf("[TCP 서버]클라이언트 접속: IP 주소=%s, 포트번호 %d\n",\
  inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));


 hThread = (HANDLE)_beginthreadex(NULL, 0, ClientContorl,(LPVOID)client_sock, 0, (unsigned *)&THreadID); //스레드 생성

 if(hThread==NULL)
  printf("Client Thread Error\n");

 WaitForSingleObject(hThread,INFINITE);  //스레드 종료를 기다림

 printf("server thread 종료\n");
 CloseHandle(hThread);
 closesocket(listen_sock);
 WSACleanup();          //윈속 종료
 return 0;
}

 

unsigned int WINAPI ClientContorl(LPVOID lpParam)//클라이언트 접속시 데이타 처리부분
{
 SOCKET client_sock = (SOCKET)lpParam;
 char buf[BUFSIZE+1];
 SOCKADDR_IN clientaddr;
 int addrlen;
 int retval;

 // client 정보 얻기
 addrlen = sizeof(clientaddr);
 getpeername(client_sock,(SOCKADDR *)&clientaddr,&addrlen);

 while(1)
 {
  //데이타 받기
  retval = recv(client_sock, buf, BUFSIZE, 0 );
  if(retval == SOCKET_ERROR)
  {
   if(WSAGetLastError()!=WSAEWOULDBLOCK)
   {
    printf("데이타 받기 실패!");
    break;
   }
  }
  else if(retval==0)
   break;
  else
  {
   //데이터 출력
   buf[retval]='\0';
   printf("[TCP/%s:%d] %s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buf);

   //데이터 보내기
   retval = send(client_sock, buf, retval, 0);
   if(retval==SOCKET_ERROR)
   {
    printf("recv error!\n");
    break;
   }
  }
 }

 closesocket(client_sock);
 printf("ClientContorl 종료 [TCP서버]:IP 주소:%s, 포트번호=%d\n"\
  ,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
 return 0;
}