-TCP의 특징-
연결 지향 - 두 컴퓨터를 연결하는 전용의 연결회선을 만든다.(세션을 생성한다)
신뢰성 있는 데이터 교환 - 전송한 데이터를 제대로 받았는지 확인 한다.
만약 실패하면 재전송 한다.
전 이중 통신 - 읽고 쓰기를 모두 할 수 있다.
-TCP의 과정-
1. 클라이언트가 패킷을 전송한다.
2. 패킷을 받은 서버는 일련 번호에 1을 더한 값을 ACK 값을 설정해서 전송한다.
3. 클라이언트는 서버의 ACK 값을 읽어서 패킷이 제대로 전달된 것을 알게 된다
4. 1~3을 반복하면서 데이터를 주고 받는다.
-소스코드-
아래 코드는 "뇌를 자극하는 TCP/IP 소켓 프로그래밍" 코드다.
꼭 책을 사서 공부하자~ 이 블로그는 공부한 내용을 기억하기 위해 요약 한 것이다.
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#define PORT 3600
//주고 받을 데이터를 구조화 하기 위해 cal_data 구조체를 정의 한다.
struct cal_data
{
int left_num;
int right_num;
char op;
int result;
short int error;
};
int main(int argc, char **argv)
{
struct sockaddr_in client_addr, sock_addr;
int listen_sockfd, client_sockfd;
int addr_len;
struct cal_data rdata;
int left_num, right_num;
short int cal_result;
//IP와 연결지향 TCP 기반 통신을 위한 듣기 소켓을 생성한다.
if( (listen_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Error ");
return 1;
}
//듣기 소켓을 위한 주소 영역, 기다릴 주소와 포트 번호를 생성한다.
memset((void *)&sock_addr, 0x00, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = htons(PORT);
//bind 함수로 소켓 특성을 묶어준다.
if( bind(listen_sockfd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) == -1)
{
perror("Error ");
return 1;
}
//수신 대기열을 생성한다.
if(listen(listen_sockfd, 5) == -1)
{
perror("Error ");
return 1;
}
for(;;)
{
addr_len = sizeof(client_addr);
//accept 함수로 수신 대기열에 클라이언트 연결이 있는지 확인한다.
client_sockfd = accept(listen_sockfd,
(struct sockaddr *)&client_addr, &addr_len);
if(client_sockfd == -1)
{
perror("Error ");
return 1;
}
//read 함수로 소켓에서 데이터를 읽어온다.
read(client_sockfd, (void *)&rdata, sizeof(rdata));
rdata.error = 0;
//읽어온 데이터는 네이퉈크 바이트 순서를 따른다.
//ntohs 함수로 호스트 바이트 순서로 변환한다.
left_num = ntohs(rdata.left_num);
right_num = ntohs(rdata.right_num);
//op 값을 확인해서 필요한 사칙연산 루틴을 실행한다.
//나누기(/)에서 분모가 되는 오른쪽 피연산자가 0이면 error 값을 설정한다.
switch(rdata.op)
{
case '+':
cal_result = left_num + right_num;
break;
case '-':
cal_result = left_num - right_num;
break;
case '*':
cal_result = left_num * right_num;
break;
case '/':
if(right_num == 0)
{
rdata.error = 2;
break;
}
cal_result = left_num / right_num;
break;
default:
rdata.error = 1;
}
//계산 값과 에러 값을 네트워크 바이트 순서에 맞게 변환한다.
rdata.result = htonl(cal_result);
rdata.error = htons(cal_result);
//write 함수로 계산 결과를 클라이언트에 전송한다.
write(client_sockfd, (void *)&rdata, sizeof(rdata));
close(client_sockfd);
}
close(listen_sockfd);
return 0;
}
댓글 없음:
댓글 쓰기