Download presentation
Presentation is loading. Please wait.
0
TCP 서버/클라이언트 Chapter 04. * 학습목표 TCP 서버/클라이언트의 기본 구조와 동작 원리를 이해함
애플리케이션 프로토콜의 필요성을 이해하고, 메시지 설계 기법을 익힘
1
TCP 서버/클라이언트 동작 원리 - (1) TCP 서버/클라이언트 예 웹 서버 웹 클라이언트 웹 클라이언트
GET / HTTP/1.1 Accept: image/gif, ... <HTML> <HEAD>...</HEAD>... 웹 서버 웹 클라이언트 웹 클라이언트
2
TCP 서버/클라이언트 동작 원리 - (2) TCP 서버/클라이언트 동작 방식 TCP 서버 TCP 클라이언트 listen
accept recv send connect 네트워크 TCP 서버/클라이언트 동작 방식 ① 서버는 먼저 실행하여 클라이언트가 접속하기를 기다린다(listen). ② 클라이언트가 서버에게 접속(connect)하여 데이터를 보낸다(send). ③ 서버는 클라이언트 접속을 수용하고(accept), 클라이언트가 보낸 데이터를 받아서(recv) 처리한다. ④ 서버는 처리한 데이터를 클라이언트에게 보낸다(send). ⑤ 클라이언트는 서버가 보낸 데이터를 받아서(recv) 자신의 목적에 맞게 사용한다.
3
TCP 서버/클라이언트 동작 원리 - (3) TCP 서버/클라이언트 동작 원리 TCP 서버 대기 클라이언트 접속 TCP
클라이언트 #1 클라이언트 접속 (1) 서버는 소켓을 생성 후 클라이언트를 기다림 포트번호 : 9000 (2) 클라이언트 접속함 (연결설정)
4
TCP 서버/클라이언트 동작 원리 - (4) TCP 서버/클라이언트 동작 원리 (계속) TCP 서버 대기 통신 TCP
클라이언트 #1 클라이언트 #2 통신 대기 (3) 클라이언트 새로운 소켓을 생성 (4) 1명의 클라이언트가 새로 접속하여 새로운 소켓을 생성한 상태
5
TCP 서버/클라이언트 동작 원리 - (5) TCP 서버/클라이언트 동작 원리 (계속) TCP 서버/클라이언트 예제 동작 방식
클라이언트 #1 대기 클라이언트 #n . . . (5) N명의 클라이언트가 새로 접속하여 새로운 소켓을 생성한 상태 (1 : 1 통신 구조) TCP 서버/클라이언트 예제 동작 방식 TCP 클라이언트 TCP 서버 fgets() send() printf() recv() 화면 화면 데이터 데이터
6
TCP 서버/클라이언트 분석 TCP/IP 소켓 통신을 위해 필요한 요소 소켓 데이터 구조체 ① 프로토콜 소켓을 생성할 때 결정
② 지역(local) IP 주소와 지역 포트 번호 서버 또는 클라이언트 자신의 주소 ③ 원격(remote) IP 주소와 원격 포트 번호 서버 또는 클라이언트가 통신하는 상대방의 주소 소켓 데이터 구조체 서버 지역 IP 주소 지역 포트 번호 원격 IP 주소 원격 포트 번호 클라이언트 애플리케이션 운영체제 네트워크 • • •
7
TCP 서버 함수 - (1) TCP 서버 함수 socket() bind() recv() send() closesocket()
connect() listen() accept() 네트워크
8
TCP 서버 함수 - (2) bind() 함수 bind() 함수 사용 예 (예제 1 참조)
서버의 지역 IP 주소와 지역 포트 번호를 결정 int bind ( SOCKET s, // 소켓생성 결과값 const struct sockaddr* name, // 소켓구조체(주소, 포트번호, 프로토콜타입) int namelen // 소켓구조체 크기 ) ; 성공: 0, 실패: SOCKET_ERROR bind() 함수 사용 예 (예제 1 참조) 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()");
9
TCP 서버 함수 - (3) listen() 함수 listen() 함수 사용 예
소켓과 결합된 TCP 포트 상태를 LISTENING으로 변경 int listen ( SOCKET s, // 소켓생성 결과값 int backlog // 연결큐의 길이 (최대 접속자수) ) ; 성공: 0, 실패: SOCKET_ERROR listen() 함수 사용 예 retval = listen (listen_sock, SOMAXCONN); if(retval == SOCKET_ERROR) err_quit("listen()");
10
TCP 서버 함수 - (4) accept() 함수 accept() 함수 사용 예
접속한 클라이언트와 통신할 수 있도록 새로운 소켓을 생성하여 리턴 접속한 클라이언트의 IP 주소와 포트 번호를 알려줌 SOCKET accept ( SOCKET s, // 소켓(클라이언트전용) 생성 결과값 struct sockaddr* addr, // 클라이언트의 IP주소와 포트번호 지정된 메모리에 저장 int* addrlen // 지정된 메모리 주소값 ) ; 성공: 새로운 소켓, 실패: INVALID_SOCKET accept() 함수 사용 예 // 데이터 통신에 사용할 변수 SOCKET client_sock; SOCKADDR_IN clientaddr; int addrlen; ... while(1){ // accept( ) addrlen = sizeof(clientaddr); client_sock = accept (listen_sock, (SOCKADDR *)&clientaddr, &addrlen); if(client_sock == INVALID_SOCKET){ err_display("accept()"); continue; }
11
TCP 서버 함수 - (5) accept() 함수 사용 예 (계속)
printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); 078 // 클라이언트와 데이터 통신 while(1){ ... } 102 // closesocket() closesocket(client_sock); printf("[TCP 서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); }
12
실습 예제 – (1) 소요시간 : 1시간 30분. 직접 코딩해서 실습할 것. 매우 중요한 예제임. 각 라인별로 소스를 분석하시오.
13
TCP 클라이언트 함수 - (1) TCP 클라이언트 함수 네트워크 socket() bind() recv() send()
closesocket() TCP 서버 TCP 클라이언트 connect() listen() accept() 네트워크
14
TCP 클라이언트 함수 - (2) connect() 함수 connect() 함수 사용 예
int connect ( SOCKET s, // 서버와 통신을 하기 위해 만든 소켓 const struct sockaddr* name, // 서버 주소, 포트번호 -> 특정 주소값에 저장 int namelen // 소켓 주소 구조체 변수의 크기 ) ; 성공: 0, 실패: SOCKET_ERROR connect() 함수 사용 예 SOCKADDR_IN serveraddr; serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(9000); serveraddr.sin_addr.s_addr = inet_addr(" "); retval = connect(sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr)); if(retval == SOCKET_ERROR) err_quit("connect()");
15
데이터 전송 함수 - (1) 소켓 데이터 구조체 서버 클라이언트 네트워크 지역 IP 주소 지역 포트 번호 원격 IP 주소
원격 포트 번호 클라이언트 애플리케이션 운영체제 네트워크 • • • 수신 버퍼 송신 버퍼
16
데이터 전송 함수 - (2) send() 함수 recv() 함수
애플리케이션 데이터를 송신 버퍼에 복사함으로써 궁극적으로 하부 프로토콜 (Ex. TCP/IP)에 의해 데이터가 전송 int send ( SOCKET s, // 통신할 대상과 연결된 소켓 const char* buf, // 보낼 데이터를 담고 있는 애플리케이션 버퍼 주소 int len, // 보낼 데이터 크기 int flags // send( ) 함수의 동작을 바꾸는 옵션 (보통 0) ); 성공: 보낸 바이트 수, 실패: SOCKET_ERROR recv() 함수 수신 버퍼에 도착한 데이터를 애플리케이션 버퍼로 복사 int recv ( SOCKET s, // 통신할 대상과 연결된 소켓 char* buf, // 받은 데이터를 저장할 애플리케이션 버퍼의 주소 int len, // 수신 버퍼로부터 복사할 최대 데이터 크기 int flags // recv( ) 함수의 동작을 바꾸는 옵션 (보통 0) ); 성공: 받은 바이트 수 또는 0 (연결 종료 시), 실패: SOCKET_ERROR
17
데이터 전송 함수 - (3) recvn() 함수 : 만약 수신할 전체 크기를 알고 있다면
037 int recvn (SOCKET s, char *buf, int len, int flags) 038 { int received; char *ptr = buf; int left = len; // left : 보낼 총 바이트 수 042 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; } 052 return (len - left); 054 }
18
데이터 전송 함수 - (4) recvn( ) 함수 동작 원리 buf ptr left len 읽은 데이터
19
데이터 전송 함수 - (5) 데이터 전송 함수 사용 예 – TCP 클라이언트 078 char buf[BUFSIZE+1];
int len; ... while(1){ // 데이터 입력 ZeroMemory(buf, sizeof(buf)); printf("\n[보낼 데이터] "); if(fgets(buf, BUFSIZE+1, stdin) == NULL) // 사용자로부터 문자열 입력 break; 088 // '\n' 문자 제거 len = strlen(buf); if(buf[len-1] == '\n') buf[len-1] = '\0'; if(strlen(buf) == 0) break;
20
데이터 전송 함수 - (6) 데이터 전송 함수 사용 예 – TCP 클라이언트 (계속) 096 // 데이터 보내기
// 데이터 보내기 retval = send(sock, buf, strlen(buf), 0); if(retval == SOCKET_ERROR){ err_display("send()"); break; } printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retval); 103 // 데이터 받기 retval = recvn(sock, buf, retval, 0); if(retval == SOCKET_ERROR){ err_display("recv()"); break; } else if(retval == 0) break; // 받은 데이터 출력 buf[retval] = '\0'; // 맨 끝에 0을 입력함. printf("[TCP 클라이언트] %d바이트를 받았습니다.\n", retval); printf("[받은 데이터] %s\n", buf); }
21
데이터 전송 함수 - (7) 데이터 전송 함수 사용 예 – TCP 서버 066 char buf[BUFSIZE+1]; ...
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; } }
22
실습 예제 – (1) 마무리 하시오.
23
애플리케이션 프로토콜과 메시지 설계 - (1) 애플리케이션 프로토콜 애플리케이션 프로토콜 예
애플리케이션 수준에서 주고 받는 데이터의 형식과 의미, 처리 방식 등을 정의한 프로토콜 애플리케이션 프로토콜 예 네트워크
24
애플리케이션 프로토콜과 메시지 설계 - (2) 메시지 정의 ① - 직선을 그리기(타입 없음)
메시지 정의 ② - 원 그리기(타입 없음) struct DrawMessage1 { int x1, y1; // 선의 시작점 int x2, y2; // 선의 끝점 int width; // 선 두께 int color; // 선 색상 }; struct DrawMessage2 { int x1, y1; // 원의 중심 좌표 int r; // 원의 반지름 int fillcolor; // 내부 색상 int width; // 선 두께 int color; // 선 색상 };
25
애플리케이션 프로토콜과 메시지 설계 - (3) 메시지 정의 ③ - 직선 및 원 그리기(타입 존재)
struct DrawMessage1 { int type; // = LINE int x1, y1; // 선의 시작점 int x2, y2; // 선의 끝점 int width; // 선 두께 int color; // 선 색상 }; struct DrawMessage2 int type; // = CIRCLE int x1, y1; // 원의 중심 좌표 int r; // 원의 반지름 int fillcolor; // 내부 색상 int width; // 선 두께 int color; // 선 색상
26
메시지 설계 시 고려 사항 경계 구분 [송신자] ① 항상 고정 길이 데이터를 보낸다.
② 경계 구분을 위해 특별한 표시(EOR; End Of Record)를 삽입한다. ③ 보낼 데이터 길이를 고정 길이 데이터로 보낸 후, 가변 길이 데이터를 이어서 보낸다. [수신자] ① 항상 고정 길이 데이터를 받는다. ② EOR이 나올 때까지 데이터를 읽은 후 처리한다. ③ 고정 길이 데이터를 읽어 뒤따라올 데이터의 길이를 알아낸다. 이 길이만큼 데이터를 읽어 처리한다. 바이트 정렬 빅 엔디안 방식으로 통일 멤버 정렬 구조체(공용체, 클래스 포함) 멤버의 시작 주소에 대한 제약 사항 #pragma pack 컴파일러 명령을 사용
27
실습 예제 – (2) 소요시간 : 1시간 30분. 직접 코딩해서 실습할 것. 앞 예제랑 비교하면서 코드 분석할 것.
Similar presentations