---------------------------클라이언트---------------------------------------------------
//데이터 통신에 사용할 변수
 char buf[BUFSIZE+1]; [각주:1]
 int len;[각주:2]

 while(1)
 {
  //데이터 입력
  ZeroMemory(buf, sizeof(buf));[각주:3]
  printf("\n[보낼 데이터]");[각주:4]
  if(fgets(buf, BUFSIZE+1, stdin) == NULL)[각주:5]
   break;[각주:6]
  //\n 문자 제거
  len = strlen(buf);[각주:7]  
  if(buf[len-1] =='\n')[각주:8]
   buf[len-1] = '\0';[각주:9]
  if(strlen(buf) == 0)[각주:10]
   break;[각주:11]

  //데이터 보내기[각주:12]
  retval = send(sock,buf,strlen(buf), 0);
  if(retval == SOCKET_ERROR)
  {
   err_display("send()");
   break;
  }

  printf("[TCP 클라이언트] %d 바이트를 보냈습니다! \n", retval);
  
  //데이터 받기[각주:13]
  retval = recvn(sock,buf,retval,0);
  if(retval == SOCKET_ERROR)
  {
  err_display("recv()");
  break;
  }
  else if(retval==0)
  break;

  //받은 데이터 출력
  buf[retval] = '\0';[각주:14]
  printf("[TCP 클라이언트] %d 바이트를 받았습니당! \n" , retval);
  printf("[받은 데이터] %s\n", buf);

 }
-----------------------------------------------------------------------------------


---------------------------서버-----------------------------------------------------
//여기서 사용한 소켓( client_sock ) 은 accept() 함수의 리턴값으로 생성된 것임을 유의하도록 하자.

char buf[BUFSIZE+1];[각주:15]
 //클라와 데이터 통신
 while(1)[각주:16]

 retval = recv(client_sock,buf,BUFSIZE,0);[각주:17]
 if(retval == SOCKET_ERROR)
 {
  err_display("recv()");
  break; 
 }

 else if(retval == 0)
 {
  break;
 }
 //받은 데이터 출력
 buf[retval] = '\0';[각주:18]
 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)
 {
  err_display("send()");
  break;
 }
 }



  1. 입력 문자열 길이를 계산할 때 사용한다. [본문으로]
  2. fgets() 함수를 이용해 사용자로부터 문자열을 입력받는다. [본문으로]
  3. 사용자가 입력한 문자열 또는 서버로부터 받은 데이터를 저장할 버퍼다. [본문으로]
  4. 사용자가 입력한 문자열 또는 서버로부터 받은 데이터를 저장할 버퍼다. [본문으로]
  5. 사용자가 입력한 문자열 또는 서버로부터 받은 데이터를 저장할 버퍼다. [본문으로]
  6. 사용자가 입력한 문자열 또는 서버로부터 받은 데이터를 저장할 버퍼다. [본문으로]
  7. \n 문자를 제거한다.데이터 출력 시 줄바꿈 여부를 서버가 결정하도록 하기 위함이다. [본문으로]
  8. \n 문자를 제거한다.데이터 출력 시 줄바꿈 여부를 서버가 결정하도록 하기 위함이다. [본문으로]
  9. 글자를 입력하지 않고 엔터 키만 눌렀을 시 루프를 빠져나간다. [본문으로]
  10. \n 문자를 제거한다.데이터 출력 시 줄바꿈 여부를 서버가 결정하도록 하기 위함이다. [본문으로]
  11. \n 문자를 제거한다.데이터 출력 시 줄바꿈 여부를 서버가 결정하도록 하기 위함이다. [본문으로]
  12. send() 함수를 호출하고 오류 처리를 한다. 블로킹 소켓을 사용하므로 send() 함수의 리턴값은 strlen(buf) 값과 같게 된다. [본문으로]
  13. recvn() 함수를 호출하고 오류처리를 한다. 서버가 보낼 데이터의 크기를 미리 알고 있으므로 recvn() 함수를 사용하는 것이 편리하다. [본문으로]
  14. 받은 데이터 끝에 \0 을 추가하고 화면에 출력한다. [본문으로]
  15. 받은 데이터를 저장할 애플리케이션 버퍼다. [본문으로]
  16. recv() 함수를 호출하고 오류 처리 한다.서버는 받을 데이터 크기를 미리 알 수 없으므로 recvn() 함수를 사용할 수 없다. [본문으로]
  17. recv() 함수의 리턴값이 0 (정상종료) 또는 SOCKET_ERROR 가 될때까지 계속 루프를 돌며 데이터를 수신한다. [본문으로]
  18. 받은 데이터 끝에 \0 을 추가하고 화면에 출력한다. [본문으로]

'Windows > Network Programming' 카테고리의 다른 글

데이터 전송 함수 및 소켓 데이터 구조체  (0) 2011.07.16
TCP Client 분석  (0) 2011.07.15
TCP Server 분석  (0) 2011.07.15
TCP Server / Client  (0) 2011.07.15

데이터 전송 함수는 크게 데이터를 보내는 함수와 데이터를 받는 함수로 구분할 수 있다.
가장 기본이 되는 함수는 send()와 recv()이며, 그 밖에 WSA*() 형태의 확장 함수가 존재한다.

소켓 데이터 구조체란?
- TCP 소켓과 연간된 데이터 구조체로서, 각각 자신과 상대방의 IP 주소와 포트 번호 외에 데이터 송수신 버퍼가 있다.
송신 버퍼는 데이터를 전송하기 전에 임시로 저장해두는 영역이고,
수신 버퍼는 받은 데이터를 애플리케이션이 처리하기 전까지 임시로 저장해두는 영역이다.
송신 버퍼와 수신 버퍼를 통틀어서 소켓 버퍼라 부르며, send(), recv() 함수는 소켓을 통해 간접적으로 소켓 버퍼를
접근할 수 있도록 만든 함수라고 볼 수 있다.


데이터 전송 함수를 사용할 때는 하부 프로토콜의 특성을 잘 알고 있어야 하는데, TCP 프로토콜은 애플리케이션이 보낸
데이터의 경계를 구분하지 않는다는 특징이 있다. 예를 들어보면,
클라이언트가 100,200,300 바이트 데이터를 차례로 보낼 경우,
서버가 100,200,300 바이트의 경계를 구분하지 못하고 350, 250 바이트 데이터를 읽을 수 있다.
따라서  TCP 서버/클라이언트를 작성할 때는 데이터 경계 구분을 위한 상호 약속이 필요하며,
이를 애플리케이션 수준에서 처리해야만 한다.


send() 함수
- 애플리케이션 데이터를 송신 버퍼에 복사함으로써 궁극적으로 하부 프로토콜(TCP/IP)에 의해 데이터가 전송되도록 한다.
send() 함수는 데이터 복사가 성공적으로 이루어지면 바로 리턴하므로, send() 함수가 성공했다고 실제 데이터 전송이
완료된 것은 아니다.

int send(
SOCKET s,
const char* buf,
int len,
int flags
);

SOCKET s,
- 통신할 대상과 연결된 소켓이다.

const char* buf,
- 보낼 데이터를 담고 있는 애플리케이션 버퍼의 주소다.

int len,
- 보낼 데이터 크기(바이트) 이다.

int flags
- send() 함수의 동작을 바꾸는 옵션이며, 보통 0을 사용한다.
- 드물게 MSG_DONTROUTE 와 MSG_OOB 을 사용하는 경우도 있다.

send() 함수는 첫번째 인자로 사용한 소켓의 특성에 따라 다음과 같이 두 종류의 성공적인 리턴을 할 수 있다.

1. 블로킹 소켓
- 지금까지 생성한 모든 소켓은 블로킹 소켓인데, 블로킹 소켓에 대해 send() 함수를 호출할 때,
송신 버퍼의 여유 공간이 send() 함수의 세 번째 인자인 len 보다 작을 경우 해당 프로세스는 대기 상태가 된다.
송신 버퍼에 충분한 공간이 생기면 프로세스는 깨어나고, len 크기만큼 데이터 복사가 이루어진 후 send() 함수가 리턴한다.
이 경우 send() 함수의 리턴 값은 len과 같게 된다.

2. 넌블로킹 소켓
- ioctlsocket() 함수를 이용하면 블로킹 소켓을 넌블로킹 소켓으로 바꿀 수 있다.
넌블로킹소켓에 대해 send() 함수를 호출하면, 송신 버퍼의 여유 공간만큼 데이터를 복사한 후 실제 복사한 데이터 바이트
수를 리턴한다. 이 경우 send() 함수의 리턴값은 최소 1, 최대 len이 된다.








recv() 함수
- 수신 버퍼에 도착한 데이터를 애플리케이션 버퍼로 복사하는 역활을 하는 함수다.

int recv(
SOCKET s,
char* buf,
int len,
int flags
);

SOCKET s,
- 통신할 대상과 연결된 소켓이다.

char* buf,
- 받은 데이터를 저장할 애플리케이션 버퍼의 주소다.

int len,
- 수신 버퍼로부터 복사할 최대 데이터 크기(바이트) 이다. 이 값은 buf가 가리키는 버퍼의 크기보다 크면 안된다.

int flags
- recv() 함수의 동작을 바꾸는 옵션이며, 보통 0을 사용한다.
- 드물게 MSG_PEEK와 MSG_OOB 를 사용하는 경우가 있다.
- recv() 함수는 수신 버퍼의 데이터를 애플리케이션 버퍼로 복사한 후 해당 데이터를 수신 버퍼에서 삭제한다.
- 그러나 MSG_PEEK 옵션을 사용하면 수신 버퍼에 데이터가 계속 남아 있게 된다.

recv() 함수는 두 종류의 성공적인 리턴을 할 수 있다.
1. 수신 버퍼에 데이터가 도달한 경우
- recv() 함수의 세 번째 인자인 len보다 크지 않은 범위 내에서 가능한 많은 데이터를 애플리케이션 버퍼로 복사한다.
- 이 경우 복사한 바이트 수가 리턴되며, 가능한 최대 리턴값은 len 이 된다.

2. 접속이 정상 종료된 경우
- 상대 애플리케이션이 closesocket() 함수를 사용하여 접속을 종료하면, TCP 프로토콜 수준에서 접속 종료를 위한
패킷 교환 절차가 일어난다. 이 경우 recv() 함수는 0을 리턴한다.
recv() 함수의 리턴값이 0인 경우를 정상 종료라 부른다. (normal close = graceful close) [각주:1]


recv() 함수 사용시 주의할 점은,
세 번째 인자인 len 으로 지정한 크기보다 작은 데이터가 애플리케이션 버퍼로 복사될 수 있다는 사실이다.
이는 TCP 가 메시지 경계를 구분하지 않는다는 특성에 기인한 것이며, 자신이 받을 데이터의 크기를 미리 알고 있다면, 이 크기만큼 받을때까지 recv() 함수를 여러번 호출해야 한다. 예제에서는 사용자 정의 함수인 recvn() 을 정의해서 처리하고 있다.


사용예)

//사용자 정의 데이터 수신 함수
int recvn(SOCKET s, char *buf, int len, int flags)[각주:2]
{
 int received;[각주:3]
 char *ptr = buf;[각주:4]
 int left = len;[각주:5]
 while( left > 0 )[각주:6]
 {
  received = recv(s,ptr,left,flags);[각주:7]
  if(received == SOCKET_ERROR)
  return SOCKET_ERROR;
  else if(received == 0)[각주:8]
  break;[각주:9]
  left -= received;[각주:10]
  ptr += received;[각주:11]
  
 }
 return (len - left);[각주:12]
}















  1. TCP 종료시에 FIN,ACK,FIN,ACK 네 개의 패킷 교환이 일어난다.그러나 때로는 FIN, FIN/ACK, ACK 세 개의 패킷이 교환되기도 한다.먼저 closesocket() 함수를 호출한 소켓의 TCP 포트는 FIN_WAIT 상태를 거친 후 사라진다.보통 FIN_WAIT 상태에 머무르는 시간은 TCP/IP 구현마다 다르지만 일반적으로 5분을 넘지 않는다. [본문으로]
  2. recv() 함수와 동일한 인자를 사용한다. [본문으로]
  3. recv() 함수의 리턴값을 저장하는 변수다. [본문으로]
  4. 포인터 변수 ptr이 애플리케이션 버퍼의 시작 주소를 가리키도록 한다. 데이터를 읽을 때마다 ptr 변수는 증가한다. [본문으로]
  5. left 변수는 아직 읽지 않은 데이터 크기를 나타낸다. [본문으로]
  6. 읽지 않은 데이터가 존재시 계속 루프를 돈다. [본문으로]
  7. recv() 함수를 호출하고, 오류가 발생시 리턴한다. [본문으로]
  8. 함수 리턴값이 0이면 정상종료, 상대가 데이터를 더 보내지 않을 것이므로 루프를 빠져나간다. [본문으로]
  9. 함수 리턴값이 0이면 정상종료, 상대가 데이터를 더 보내지 않을 것이므로 루프를 빠져나간다. [본문으로]
  10. 각각 변수를 갱신한다. [본문으로]
  11. 읽은 바이트 수를 리턴한다.정상 종료를 제외하면 left 변수는항상 0이므로 리턴값은 len이 된다. [본문으로]
  12. 각각 변수를 갱신한다. [본문으로]

'Windows > Network Programming' 카테고리의 다른 글

send(), recv() 사용 분석  (0) 2011.07.16
TCP Client 분석  (0) 2011.07.15
TCP Server 분석  (0) 2011.07.15
TCP Server / Client  (0) 2011.07.15

일반적으로 TCP 클라이언트는 다음의 순서를 가진다.

1. socket()
2. connect()
3. send(), recv() 등 통신을 수행
4. closesocket() 를 이용해 소켓을 닫는다.

connect() 함수
- 클라이언트가 서버에 접속하여 TCP 프로토콜 수준의 연결이 이루어지도록 한다.

int connect(
SOCKET s,
const struct sockaddr* name,
int namelen
);

SOCKET s,
- 서버와 통신을 위해 만든 소켓

const struct sockaddr* name,
- 소켓 주소 구조체 변수를 서버 주소(원격 IP , 원격 포트번호) 로 초기화한 후 ,이 변수의 주소값을 여기에 대입한다.

int namelen
- 소켓 주소 구조체 변수의 길이(바이트) 를 대입한다.

클라이언트는 서버와 달리 bind() 함수를 호출하지 않으며, 그 상태에서 connect() 함수를 호출하면 운영체제는 자동으로
지역 IP 주소와 지역 포트 번호를 설정한다. 이때 자동으로 할당되는 포트 번호는 운영체제에 따라 다를 수 있으며,
윈도우일 경우 1024~5000 중 하나가 할당된다.


사용예)
 //connect()
 SOCKADDR_IN serveraddr;[각주:1]
 serveraddr.sin_family = AF_INET;[각주:2]
 serveraddr.sin_port = htons(9000);[각주:3]
 serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");[각주:4]
 retval = connect(sock, (SOCKADDR *) &serveraddr, sizeof(serveraddr));
 if(retval == SOCKET_ERROR ) err_quit("connect()");










  1. 소켓 주소 구조체 변수를 초기화한다. [본문으로]
  2. 소켓 주소 구조체 변수를 초기화한다. [본문으로]
  3. 소켓 주소 구조체 변수를 초기화한다. [본문으로]
  4. 원격 IP주소나 루프백 주소를 대입하면 된다. [본문으로]

'Windows > Network Programming' 카테고리의 다른 글

send(), recv() 사용 분석  (0) 2011.07.16
데이터 전송 함수 및 소켓 데이터 구조체  (0) 2011.07.16
TCP Server 분석  (0) 2011.07.15
TCP Server / Client  (0) 2011.07.15

서버쪽 함수

1. socket() 함수를 이용해 생성
2. bind() 함수를 이용해 지역IP 와 지역 포트 번호 결정
3. listen() 함수를 이용해 TCP상태를 LISTENING 상태로 변경
4. accpet() 함수를 이용해 접속한 클라이언트와 통신할 수 있는 새로운 소켓을 생성. 이때 원격IP주소와 포트번호 결정됨
5. send(), recv() 등의 함수를 이용해 통신을 수행,
6. closesocket() 함수를 이용해 소켓을 종료한다.

새로운 클라이언트가 접속시 4~6의 과정을 반복한다.

1. bind()
int bind(
SOCKET s,
const struct sockaddr* name,
int namelen
);

SOCKET s,
- 클라이언트 접속을 수용할 목적으로 만든 소켓이며, 지역 IP주소와 포트번호가 아직 결정되지 않는다.

const struct sockaddr* name,
- 소켓 주소 구조체(TCP일 경우 SOCKADDR_IN 타입) 변수를 지역 IP주소와 지역 포트 번호로 초기화 후,
이 변수의 주소값을 여기에 대입한다.

int namelen
- 소켓 주소 구조체 변수의 길이(바이트) 를 대입한다.

예문)

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);
 retval = bind(listen_sock, (SOCKADDR *) &serveraddr, sizeof(serveraddr));
 if(retval == SOCKET_ERROR ) err_quit("bind()");

// 소켓 주소 구조체 변수를 선언후에 0으로 초기화한다. ZeroMemory() (API 함수) 또는 memset()을 이용한다.
// 인터넷 주소 체계를 사용한다는 의미로 AF_INET을 대입한다.
// 포트 번호를 9000번으로 설정하며, htons() 함수로 바이트 정렬을 변경한 값을 대입한다.
// 서버의 지역 IP 주소를 설정한다. 서버의 경우 특정 IP 주소보다는 INADDR_ANY 값을 사용하는 것이 바람직하다.
// 서버가 2개 이상일 경우 INADDR_ANY 값을 지역 주소로 설정하면 클라이언트가 어느 주소로 접속하든 처리 가능하다.
// bind() 함수를 호출하고, 두번째 인자는 항상 (SOCKADDR*) 타입으로 반환해야 한다.



2. listen() 함수
int listen(
SOCKET s,
int backlog
);

SOCKET s,
- 클라이언트 접속을 수용할 목적으로 만든 소켓으로, bind() 함수에 의해 지역 IP 주소와 지역 포트 번호가 설정된 상태이다.

int backlog
- 서버가 당장 처리하지 않더라도 접속 가능한 클라이언트의 수다. 클라이언트의 접속 정보는 연결 큐에 저장되며,
backlog 는 이 연결 큐의 길이를 나타낸다고 보면 된다. 하부 프로토콜에서 지원 가능한 최대값을 사용시 SOMAXCONN 대입.


3. accept() 함수
- 서버에 접속한 클라이언트와 통신할 수 있도록 새로운 소켓을 생성 후, 리턴하는 역활을 한다.
또한 접속한 클라이언트의 IP 주소와 포트 번호를 알려준다. 클라이언트 입장에서는 지역 IP 주소와 포트번호.
SOCKET accept(
SOCKET s,
struct sockaddr* addr,
int* addrlen
);

SOCKET s,
- 클라이언트 접속을 수용할 목적의 소켓

struct sockaddr* addr,
- 소켓 주소 구조체 변수를 정의한 후, 이 변수의 주소값을 여기에 대입한다.
- accpet() 함수는 addr이 가리키는 메모리 영역을 클라이언트의 IP 주소와 포트 번호로 채워 넣는다.

int* addrlen
- 정수형 변수를 addr이 가리키는 메모리 영역의 크기로 초기화한 후, 이 변수의 주소값을 여기에 대입한다.
accept() 함수가 리턴하면 정수형 변수는 addrlen은 함수가 초기화한 메모리 크기값(바이트) 을 가진다.

// 클라이언트의 IP 주소와 포트 번호를 알 필요가 없다면 addr과 addrlen에 NULL을 사용하면 된다.


사용예)

//데이터 통신에 사용할 변수
 SOCKET client_sock;[각주:1]
 SOCKADDR_IN clientaddr;[각주:2]
 int addrlen;[각주:3]
 char buf[BUFSIZE+1];
 
 while(1)[각주:4]
 {
  //accept
  addrlen = sizeof(clientaddr);[각주:5]
  client_sock = accept(listen_sock, (SOCKADDR*) &clientaddr , &addrlen);[각주:6]
  if(client_sock == INVALID_SOCKET )
  {
   err_display("accpet()");
   continue;
  }
  printf("\n[TCP 서버] 클라이언트 접속 : IP 주소 : %s, 포트번호 : %d\n",
    inet_ntoa(clientaddr.sin_addr),
    ntohs(clientaddr.sin_port ));
    


 while(1)
 {
//클라와 데이터 통신[footnote][/footnote]
 }
 
closesocket(client_sock);
printf("\n[TCP Server] 클라이언트 종료 : IP 주소 : %s , 포트번호 : %d\n",
   inet_ntoa(clientaddr.sin_addr),
   ntohs(clientaddr.sin_port ));
}




  1. accept() 함수의 리턴값을 저장할 SOCKET 타입 변수다. [본문으로]
  2. accpet() 함수의 두 번째 인자로 사용되며 accept() 함수 리턴 후 클라이언트의 IP 주소와 포트 번호가 여기에 저장된다. [본문으로]
  3. accept() 함수의 세번째 인자로 사용된다. [본문으로]
  4. 일반적으로 서버는 계속클라이언트 요청을 처리해야 하므로 무한 루프를 돈다. [본문으로]
  5. accept() 함수를 호출하기 전에, 세 번째 인자로 사용할 정수형 변수 addrlen을 소켓 주소 구조체 변수(clientaddr)의 크기로 초기화한다. [본문으로]
  6. accept() 함수를 호출하고 오류 처리를 한다. 이전에 사용한 소켓 함수와 달리 오류가 발생시 err_display() 함수를 이용해 화면에 표시한 후, 다시 돌아간다. 심각한 오류가 아니면 이와 같이 서버를 계속 구동하는 것이 바람직하다. [본문으로]

'Windows > Network Programming' 카테고리의 다른 글

send(), recv() 사용 분석  (0) 2011.07.16
데이터 전송 함수 및 소켓 데이터 구조체  (0) 2011.07.16
TCP Client 분석  (0) 2011.07.15
TCP Server / Client  (0) 2011.07.15

1.  Client Source


#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFSIZE 512

//소켓 함수 오류 출력 후 종료
void err_quit(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
   FORMAT_MESSAGE_ALLOCATE_BUFFER |
   FORMAT_MESSAGE_FROM_SYSTEM ,
   NULL,
   WSAGetLastError(),
   MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   (LPTSTR)&lpMsgBuf,0,NULL);
 MessageBox(NULL,(LPTSTR)lpMsgBuf,msg,MB_ICONERROR);
 LocalFree(lpMsgBuf);
 exit(-1);
  
}

//소켓 함수 오류 출력

void err_display(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
   FORMAT_MESSAGE_ALLOCATE_BUFFER |
   FORMAT_MESSAGE_FROM_SYSTEM,
   NULL,WSAGetLastError(),
   MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   (LPTSTR)&lpMsgBuf, 0, NULL);
 printf("[%s] %s",msg,(LPCTSTR)lpMsgBuf);
 LocalFree(lpMsgBuf);
  
}

//사용자 정의 데이터 수신 함수
int recvn(SOCKET s, char *buf, int len, int flags)
{
 int received;
 char *ptr = buf;
 int left = len;

 while( left > 0 )
 {
  received = recv(s,ptr,left,flags);
  if(received == SOCKET_ERROR)
  return SOCKET_ERROR;
  else if(received == 0)
  break;
  left -= received;
  ptr += received;
  
 }
 return (len - left);
}

 

 


int main(int argc, char* argv[])
{
 int retval;

 //윈속 초기화
 WSADATA wsa;
 if(WSAStartup(MAKEWORD(2,2) , &wsa) != 0)
  return -1; // 0이 아닐 시 -1리턴
 
 //socket()
 SOCKET sock =socket(AF_INET,SOCK_STREAM,0);
 if(sock == INVALID_SOCKET) err_quit("socket()");
 
 //connect()
 SOCKADDR_IN serveraddr;
 serveraddr.sin_family = AF_INET;
 serveraddr.sin_port = htons(9000);
 serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 retval = connect(sock, (SOCKADDR *) &serveraddr, sizeof(serveraddr));
 if(retval == SOCKET_ERROR ) err_quit("connect()");


 //데이터 통신에 사용할 변수
 int len;
 char buf[BUFSIZE+1];
 
 while(1)
 {
  //데이터 입력
  ZeroMemory(buf, sizeof(buf));
  printf("\n[보낼 데이터]");
  if(fgets(buf, BUFSIZE+1, stdin) == NULL)
   break;
  //\n 문자 제거
  len = strlen(buf);
  
  if(buf[len-1] =='\n')
   buf[len-1] = '\0';
  if(strlen(buf) == 0)
   break;

  //데이터 보내기
  retval = send(sock,buf,strlen(buf), 0);
  if(retval == SOCKET_ERROR)
  {
   err_display("send()");
   break;
  }

  printf("[TCP 클라이언트] %d 바이트를 보냈습니다! \n", retval);
  
  //데이터 받기
  retval = recvn(sock,buf,retval,0);
  if(retval == SOCKET_ERROR)
  {
  err_display("recv()");
  break;
  }
  else if(retval==0)
  break;

  //받은 데이터 출력
  buf[retval] = '\0';
  printf("[TCP 클라이언트] %d 바이트를 받았습니당! \n" , retval);
  printf("[받은 데이터] %s\n", buf);

 }

 //closesocket()

 closesocket(sock);

 


 //윈속 종료
 WSACleanup();
 return 0;


}





2. Server Source


#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFSIZE 512

//소켓 함수 오류 출력 후 종료
void err_quit(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
   FORMAT_MESSAGE_ALLOCATE_BUFFER |
   FORMAT_MESSAGE_FROM_SYSTEM ,
   NULL,
   WSAGetLastError(),
   MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   (LPTSTR)&lpMsgBuf,0,NULL);
 MessageBox(NULL,(LPTSTR)lpMsgBuf,msg,MB_ICONERROR);
 LocalFree(lpMsgBuf);
 exit(-1);
  
}

//소켓 함수 오류 출력

void err_display(char *msg)
{
 LPVOID lpMsgBuf;
 FormatMessage(
   FORMAT_MESSAGE_ALLOCATE_BUFFER |
   FORMAT_MESSAGE_FROM_SYSTEM,
   NULL,WSAGetLastError(),
   MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   (LPTSTR)&lpMsgBuf, 0, NULL);
 printf("[%s] %s",msg,(LPCTSTR)lpMsgBuf);
 LocalFree(lpMsgBuf);
  
}

int main(int argc, char* argv[])
{
 int retval;

 //윈속 초기화
 WSADATA wsa;
 if(WSAStartup(MAKEWORD(2,2) , &wsa) != 0)
  return -1; // 0이 아닐 시 -1리턴
 
 //socket()
 SOCKET listen_sock =socket(AF_INET,SOCK_STREAM,0);
 if(listen_sock == INVALID_SOCKET) err_quit("socket()");
 
 //bind()
 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);
 retval = bind(listen_sock, (SOCKADDR *) &serveraddr, sizeof(serveraddr));
 if(retval == SOCKET_ERROR ) err_quit("bind()");
 
 //listen
 retval = listen(listen_sock, SOMAXCONN);
 if(retval == SOCKET_ERROR ) err_quit("listen()");

 //데이터 통신에 사용할 변수
 SOCKET client_sock;
 SOCKADDR_IN clientaddr;
 int addrlen;
 char buf[BUFSIZE+1];
 
 while(1)
 {
  //accept
  addrlen = sizeof(clientaddr);
  client_sock = accept(listen_sock, (SOCKADDR*) &clientaddr , &addrlen);
  if(client_sock == INVALID_SOCKET )
  {
   err_display("accpet()");
   continue;
  }
  printf("\n[TCP 서버] 클라이언트 접속 : IP 주소 : %s, 포트번호 : %d\n",
    inet_ntoa(clientaddr.sin_addr),
    ntohs(clientaddr.sin_port ));
    
 

 //클라와 데이터 통신
 while(1)
 {
 
 
 retval = recv(client_sock,buf,BUFSIZE,0);
 if(retval == SOCKET_ERROR)
 {
  err_display("recv()");
  break; 
 }

 else if(retval == 0)
 {
  break;
 }
 //받은 데이터 출력
 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)
 {
  err_display("send()");
  break;
 }
 }

 //closesocket()

 closesocket(client_sock);
 printf("\n[TCP Server] 클라이언트 종료 : IP 주소 : %s , 포트번호 : %d\n",
   inet_ntoa(clientaddr.sin_addr),
   ntohs(clientaddr.sin_port ));
}
 //closesocket()
 closesocket(listen_sock);

 //윈속 종료
 WSACleanup();
 return 0;


}


'Windows > Network Programming' 카테고리의 다른 글

send(), recv() 사용 분석  (0) 2011.07.16
데이터 전송 함수 및 소켓 데이터 구조체  (0) 2011.07.16
TCP Client 분석  (0) 2011.07.15
TCP Server 분석  (0) 2011.07.15

+ Recent posts