본문 바로가기
코스웨어/10년 시스템제어

11번 박동원 네트워크

by 알 수 없는 사용자 2010. 4. 8.
728x90
반응형

TCP 서버/클라이언트 동작 방식 (cont’d)

① 서버는 먼저 실행하여 클라이언트가 접속하기를 기다린다(listen).

② 클라이언트가 서버에게 접속(connect)하여 데이터를 보낸다(send).
③ 서버는 클라이언트 접속을 수용하고(accept), 클라이언트가 보낸 데이터를 받아서(recv) 처리한다.
④ 서버는 처리한 데이터를 클라이언트에게 보낸다(send).
⑤ 클라이언트는 서버가 보낸 데이터를 받아서(recv) 자신의 목적에 맞게 사용한다



TCP/IP 소켓 통신을 위해 필요한 요소

① 프로토콜

    -소켓을 생성할 때 결정
② 지역(local) IP 주소와 지역 포트 번호
    -서버 또는 클라이언트 자신의 주소
③ 원격(remote) IP 주소와 원격 포트 번호
    -서버 또는 클라이언트가 통신하는 상대방의 주소



//10번 분이 언급한 부분은 과감히 생략!!!
//sever.cpp


#include "stdafx.h"

#include <winsock2.h> //windows.h보다 먼저 선언해야함
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#pragma comment(lib, "ws2_32.lib"


#define BUFSIZE 512

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



int main(int argc, char* 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");
  
  
  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");
    
    
/*
    accept()함수
    접속한 클라이언트와 통신할 수 있도록 새로운 소켓을 생성하여 리턴, 함수가 알아서 소켓생성
    접속한 클라이언트의 IP 주소와 포트 번호를 알려줌
    
    SOCKET accept (SOCKET s, struct sockaddr *addr, int *addrlen);
    s : 서버 소켓 파일의 파일 디스크립터를 인자로 전달 
    addr : 연결 요청을 수락할 클라이언트의 기본 주소 정보를 저장할 포인터 변수, 함수 호출이 
        성공하게되면 addr이 가리키는 변수에는 클라이언트의 주소 정보로 채워지게된다 
    addrlen : 함수 호출시 인자로 전달괸 addr포인터가 가리키는 구조체의 크기를 저장하고 있는 
        변수의 포인터(주소)를 넘겨준다 그러나 함수 호출이 성공적으로 끝나게되면 addrlen 포인터가 
        가리키는 변수 안에는 리턴받은 클라이언트의 주소 정보 길이가 바이트 단위로 채워지게 된다 
    */

    client_sock = accept(listen_sock,(SOCKADDR *)&clientaddr,&addrlen);

    if(client_sock ==INVALID_SOCKET)
    {
      printf("accept error\n");
    }
    Sleep(1);
  }while(client_sock==INVALID_SOCKET);
  
  printf("[TCP 서버]클라이언트 접속: IP 주소=%s, 포트번호 %d\n",\
    inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
  
  
   //스레드 생성,ClientContorl함수 처리를 위해 
  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;
  char END[] = "END";
  // client 정보 얻기
  addrlen = sizeof(clientaddr);
  
  //client 소켓을 통해서 정보를 얻어온다 
  getpeername(client_sock,(SOCKADDR *)&clientaddr,&addrlen);
  
  while(1)
  {
    
    //데이타 받기
    
/*
    recv() 함수
    수신 버퍼에 도착한 데이터를 애플리케이션 버퍼로 복사
    
    int recv (SOCKET s, char* buf, int len, int flags);
       s : 데이터를 수신할 영역을 나타내는 소켓의 핸들
    buf : 수신할 데이터를 저장할 버퍼를 가리키는 포인터
    len : 수신할 최대 바이트수 
    flags : 함수 호출 시 여러가지 옵션을 설정하기 위해서 사용
    성공 : 받은 바이트 수 또는 0(연결 종료시) 
    실패 : SOCKET_ERROR
    */

    retval = recv(client_sock, buf, BUFSIZE, 0 );
    
    if(retval == SOCKET_ERROR)
    {
      printf("데이타 받기 실패!");
      break;
    }
    else if(retval==0)
      break;
    
    

    //데이터 출력
    buf[retval]='\0';
    
    //클라이언트에서 END 가 왔을경우 프로그램 종료
    if ((buf[0=='E'&& (buf[1=='N')&&(buf[2=='D')&&(buf[3=='\0'))
      return 0;
    printf("[TCP/%s:%d] %s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),buf);
    
    int length = strlen(buf);
    for(int i = 0; i <= length; i++) {
      if((buf[i] >= 'A'&& (buf[i] <= 'Z'))
        buf[i] = buf[i] + 32;    //대문자 소문자 변환
      
      else if((buf[i] >= 'a'&& (buf[i] <= 'z'))
        buf[i] = buf[i] - 32;    //소문자 대문자 변환
    }
    
    //데이터 보내기
    
/*
    send() 함수
    데이터를 송신 버퍼에 복사함으로써 궁극적으로 하부 프로토콜(예를 들면, TCP/IP)에 의해 
    데이터가 전송되도록 함
    
    int send (SOCKET s, const char* buf, int len, int flags);
    s : 데이터를 전송할 호스트에 연결된 소켓의 핸들을 인자로 전달한다 
    buf : 전송할 데이터를 저장하고 있는 버퍼를 가리키는 포인터
    len : 전송할 바이트수를 인자로 전달한다 
    flags : 함수 호출 시 여러가지 옵션을 설정하기 위해서 사용
    성공 : 받은 바이트 수 또는 0(연결 종료시) 
    실패 : SOCKET_ERROR
    */


    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;
}




//client.cpp
#include "stdafx.h"
#include <winsock2.h> //windows.h보다 먼저 선언해야함
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")

#define BUFSIZE 512

unsigned int WINAPI ClientProc(LPVOID lpParam);

int main(int argc, char* argv[])
{
  DWORD dwID;
  
  HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ClientProc, NULL, 0, (unsigned *)&dwID); //스레드 생성
  
  WaitForSingleObject(h,INFINITE);  //스레드 종료를 기다림 
  
  printf("Main Thread 종료\n");
  
  CloseHandle(h);           //핸들을 반환
  return 0;
}

unsigned int WINAPI ClientProc(LPVOID lpParam)

  WSADATA wsa;
  int retval;
  
  //윈속 초기화
  if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)
    return -1;
  
  SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
  
  if(sock == INVALID_SOCKET) 
    printf("소켓 초기화 실패\n");
  
  //serversock setting
  SOCKADDR_IN serveraddr;
  serveraddr.sin_family =AF_INET;
  serveraddr.sin_port=htons(9000);
  //loopback addr 
  serveraddr.sin_addr.s_addr=inet_addr("192.168.10.27");
  
  //connect
  
/*
  connect() 함수
  서버에게 접속하여 TCP 프로토콜 수준의 연결 설정
  
  int connect (SOCKET s, const struct sockaddr *sever_addr, int addrlen) ;
  s : 미리생성해 놓은 소켓의파일 디스크립터이다 클라이언트도 연결을 요청하고
    데이터를 송,수신하기 위해서는 기본적으 소켓이 있어야 한다
  serv_addr : 연결 요청을 보낼 서버 주소 정보를 지닌 구조체 포인터 변수
  addrlen : serv_addr 포인터가 가리키는 주소 정보 구조체 변수의 크기가 된다 
  성공: 0, 실패: SOCKET_ERROR
  connect함수가 리턴되는 시점은 연결 요청이 서버에 의해 수락되거나, 오류가 발생하여
  연결 요청이 중단되는 경우이다 만일 연결 요청이 바로 이루어지지 않고 서버의 대기큐에서
  대기하고 있는 상태라면 connect 함수는 리턴되지 않고 블로킹(Blocking)상태에 있게 된다
  */


  retval=connect(sock,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
  if(retval==SOCKET_ERROR)
  {
    printf("소켓 연결 실패\n");
    return 0;
  }
  
  // 데이타 통신 시작
  char buf[BUFSIZE+1];
  int len;
  
  while(1)
  {
    ZeroMemory(buf,sizeof(buf));
    printf("\n send data:");
    
    if(fgets(buf,BUFSIZE+1,stdin)==NULL)
      break;
    
    len=strlen(buf);
    if(len-1==0)
    { 
      printf("통신을 종료합니다.\n");
      break;
    }
    
else
      buf[len]='\0';
        
    
/*
    send() 함수
    데이터를 송신 버퍼에 복사함으로써 궁극적으로 하부 프로토콜(예를 들면, TCP/IP)에 의해 
    데이터가 전송되도록 함
    
    int send (SOCKET s, const char* buf, int len, int flags);
    s : 데이터를 전송할 호스트에 연결된 소켓의 핸들을 인자로 전달한다 
    buf : 전송할 데이터를 저장하고 있는 버퍼를 가리키는 포인터
    len : 전송할 바이트수를 인자로 전달한다 
    flags : 함수 호출 시 여러가지 옵션을 설정하기 위해서 사용
    성공 : 받은 바이트 수 또는 0(연결 종료시) 
    실패 : SOCKET_ERROR
    */

    
    retval= send(sock,buf,strlen(buf),0);
    if(retval==SOCKET_ERROR)
    {
      printf("데이타 전송 에러.\n");
      break;
    }
    
    printf("[TCP 클라이언트] %d 바이트를 전송 하였습니다.\n",retval);
    
    
/*
    recv() 함수
    수신 버퍼에 도착한 데이터를 애플리케이션 버퍼로 복사
    
    int recv (SOCKET s, char* buf, int len, int flags);
       s : 데이터를 수신할 영역을 나타내는 소켓의 핸들
    buf : 수신할 데이터를 저장할 버퍼를 가리키는 포인터
    len : 수신할 최대 바이트수 
    flags : 함수 호출 시 여러가지 옵션을 설정하기 위해서 사용
    성공 : 받은 바이트 수 또는 0(연결 종료시) 
    실패 : SOCKET_ERROR
    */

    
    retval=recv(sock,buf,retval,0);
    if(retval==SOCKET_ERROR)
    { 
      printf("데이타 수신 에러\n");
      break;
    }
        
    buf[retval]='\0';
    
    printf("[TCP 클라이언트] %d 바이트를 전송받았습니다.\n",retval);
    printf("[Recv Data] : %s\n",buf);
  }
  
  //close socket
  closesocket(sock);
  //윈속 종료
  WSACleanup();
  
  
return 0;
}




728x90