2016년 5월 14일 토요일

뇌자극 TCP_IP 프로그래밍 8강 요약

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

댓글 없음:

댓글 쓰기