C/TCPIP 소켓 프로그래밍(윤성우 저)

TCP/IP 소켓 프로그래밍 2장 내용 확인문제

꾸준함. 2017. 5. 16. 16:16

1.프로토콜이란 무엇을 의미하는가? 그리고 데이터의 송수신에 있어서 프로토콜을 정의한다는 것은 어떠한 의미가 있는가?


>프로토콜은 통신의 방법을 약속해 놓은 통신규약을 의미한다. 따라서 프로토콜을 정의한다는 것은 데이터 송수신에 필요한 약속을 정의한다는 의미이다.


2.연결지향형 소켓인 TCP 소켓의 특성 세가지를 나열하라


>a. 중간에 데이터가 소멸되지 않고 목적지로 전송된다.

  b. 전송 순서대로 상대 호스트로 데이터가 수신된다.

  c. 전송되는 데이터의 경계가 존재하지 않는다.(여러 차례 나누어서 데이터를 보내도 한번에 데이터를 수신할 수 있다.)

  

  *반면, UDP 소켓 같은 경우 여러 차례 나누어서 데이터를 보내면 여러 차례 나누어서 데이터를 수신해야 한다.

   (전송되는 데이터의 경계가 존재)


3. 다음 중 비 연결지향형 소켓의 특성에 해당하는 것을 모두 고르면?


>a. 전송된 데이터는 손실될 수 있다.

  c. 가장 빠른 전송을 목표로 한다.

  e. 연결지향형 소켓과 달리 연결이라는 개념이 존재하지 않는다.


4. 다음 유형의 데이터 송수신에 적합한 타입의 소켓은 무엇인지 결정하고, 그러한 결정을 하게 된 이유를 설명해보자


>a. 서태지와 아이들의 실시간 라이브 방송 멀티미디어 데이터

  ┗>데이터의 손실에 덜 민감하고 전송 속도가 중요하므로, 비연결 지향형 소켓(UDP)


  b. 철수가 압축한 텍스트 파일의 전송

  ┗>압축파일은 데이터가 손실되면 안되기 때문에 연결 지향형 소켓(TCP)


  c. 인터넷 뱅킹을 이용하는 고객과 은행 사이에서의 데이터 송수신

  ┗>고객과 은행 사이간의 데이터 송수신 횟수가 많지 않다. 그리고 송수신되는 데이터의 크기도 크지 않지만 데이터의 정

       보가 손실되거나 수정되면 안된다. 따라서 연결 지향형 소켓(TCP)


5. 데이터의 경계(Boundary)가 존재하지 않는 소켓은 어떠한 타입의 소켓인가? 그리고 이러한 소켓은 데이터를 수신할 때 무엇을 주의해야 하는지 서술해보자


>연결 지향형 소켓(TCP)은 송수신 데이터의 경계가 존재하지 않는다. 따라서 입출력 함수의 호출횟수는 중요하지 않다. 중요한 것은 데이터의 송수신 양이다. 그러므로 송신된 데이터의 양과 수신된 데이터의 양이 일치하도록 코드를 작성해야합니다.

(함수 호출횟수에 의존하는 코드 X)


6. 이번에는 서버가 여러차례의 write 함수호출을 통해서 전송한 문자열을 클라이언트에서 한번의 read 함수호출을 통해서 읽어들이는 형태로 예제를 작성해보자.(단, 이를 위해서 클라이언트는 read 함수의 호출 시기를 다소 늦출 필요가 있다.)


<리눅스 서버 프로그램>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/types.h>

#include <sys/socket.h>

 

void error_handling(char *message);

 

int main(int argc, char *argv[])

{

        int serv_sock;

        int clnt_sock;

 

        struct sockaddr_in serv_addr;

        struct sockaddr_in clnt_addr;

        socklen_t clnt_addr_size;

 

        char message[] = "Hello World!";

 

        if (argc != 2) {

               printf("Usage : %s <port>\n", argv[0]);

               exit(1);

        }

 

        serv_sock = socket(PF_INET, SOCK_STREAM, 0); //socket 함수호출을 통해서 소켓을 생성

        if (serv_sock == -1)

               error_handling("socket() error");

 

        memset(&serv_addr, 0, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;

        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        serv_addr.sin_port = htons(atoi(argv[1]));

 

        if (bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) //bind 함수호출을 통해서 IP주소와 PORT번호를 할당

               error_handling("bind() error");

 

        if (listen(serv_sock, 5) == -1) //listen 함수를 호출, 이로써 소켓은 연결요청을 받아들일 있는 상태

               error_handling("listen() error");

 

        clnt_addr_size = sizeof(clnt_addr);

        clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); //연결요청의 수락을 위한 accept함수 호출

        //연결요청이 없는 상태에서 이함수가 호출되면, 연결요청이 있을 때까지 함수는 반환하지 않는다

        if (clnt_sock == -1)

               error_handling("accept() error");

 

        //메세지를 나누어서 보낸다

        write(clnt_sock, message, 4);

        write(clnt_sock, message + 4, 4);

        write(clnt_sock, message + 8, 4);

        write(clnt_sock, message + 12, sizeof(message) - 12);

 

        close(clnt_sock);

        return 0;

}

 

void error_handling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

서버 프로그램을 컴파일하면 화면이 멈추고 커서만 깜빡인다.(연결요청이 있을때까지 기다린다)


<리눅스 클라이언트 프로그램>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/types.h>

#include <sys/socket.h>

 

void error_handling(char *message);

 

int main(int argc, char* argv[])

{

        int sock;

        struct sockaddr_in serv_addr;

        char message[30];

        int str_len = 0;

        int idx = 0, read_len = 0, i;

 

        if (argc != 3) {

               printf("Usage : %s <IP> <port>\n", argv[0]);

               exit(1);

        }

 

        sock = socket(PF_INET, SOCK_STREAM, 0); //소켓을 생성

        //소켓을 생성하는 순간에는 서버 소켓과 클라이언트 소켓으로 나누어지지 않는다.

        //bind, listen 함수의 호출이 이어지면 서버 소켓이 되는 것이고, connect 함수의 호출로 이어지면 클라이언트 소켓이 되는 것이다

        if (sock == -1)

               error_handling("socket() error");

 

        memset(&serv_addr, 0, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;

        serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

        serv_addr.sin_port = htons(atoi(argv[2]));

 

        if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) //connect 함수호출을 통해서 서버프로그램에 연결을 요청

               error_handling("connect() error!");

 

        /*

        클라이언트가 read 함수의 호출 시기를 늦추는 이유

        ->서버가 데이터를 모두 전송할 때까지 기다려야 하기 때문이다.

        */

        for (i = 0; i<100; i++)              

               printf("Wait time %d \n", i);

 

        read(sock, message, sizeof(message)); //데이터의 경계가 존재하지 않으므로 한번에 수신 가능

        printf("Message from server: %s \n", message);

        close(sock);

        return 0;

}

 

void error_handling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

서버가 데이터를 모두 전송할 때까지 충분히 시간을 준 다음 서버로부터 데이터를 받고 출력한다. Hello World!


<윈도우 서버 프로그램>

#include <stdio.h>

#include <stdlib.h>

#include <WinSock2.h>

 

void ErrorHandling(char *message);

 

int main(int argc, char *argv[])

{

        WSADATA wsaData;

        SOCKET hServSock, hClntSock;

        SOCKADDR_IN servAddr, clntAddr;

 

        int szClntAddr;

        char message[] = "Hello World!";

        if (argc != 2)

        {

               printf("Usage:%s <port>\n", argv[0]);

               exit(1);

        }

 

        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) //소켓 라이브러리 초기화

               ErrorHandling("WSAStartup() error!");

 

        hServSock = socket(PF_INET, SOCK_STREAM, 0); //소켓생성

        if (hServSock == INVALID_SOCKET)

               ErrorHandling("socket() error");

 

        memset(&servAddr, 0, sizeof(servAddr));

        servAddr.sin_family = AF_INET;

        servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

        servAddr.sin_port = htons(atoi(argv[1]));

 

        if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) //소켓에 IP주소와 PORT 번호 할당

               ErrorHandling("bind() error");

 

        if (listen(hServSock, 5) == SOCKET_ERROR) //listen 함수호출을 통해서 생성한 소켓을 서버 소켓으로 완성

               ErrorHandling("listen() error");

 

        szClntAddr = sizeof(clntAddr);

        hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr); //클라이언트 연결요청 수락하기 위해 accept함수 호출

        if (hClntSock == INVALID_SOCKET)

               ErrorHandling("accept() error");

 

        send(hClntSock, message, 4, 0); //send함수 호출을 통해서 연결된 클라이언트에 데이터를 나누어서 전송

        send(hClntSock, message + 4, 4, 0);

        send(hClntSock, message + 8, 4, 0);

        send(hClntSock, message + 12, sizeof(message) - 12, 0);

        closesocket(hClntSock);

        closesocket(hServSock);

        WSACleanup(); //프로그램 종료 전에 초기화한 소켓 라이브러리 해제

        return 0;

}

 

void ErrorHandling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

리눅스와 마찬가지로 클라이언트 연결이 될 때까지 실행창이 멈추고 커서만 깜빡인다


<윈도우 클라이언트 프로그램>

#include <stdio.h>

#include <stdlib.h>

#include <WinSock2.h>

 

void ErrorHandling(char *message);

 

int main(int argc, char *argv[])

{

        WSADATA wsaData;

        SOCKET hSocket; //socket 함수의 반환 저장을 위해서 SOCKET 변수 하나를 선언

        SOCKADDR_IN servAddr;

 

        char message[30];

        int strLen = 0;

        int idx = 0, i;

 

        if (argc != 3)

        {

               printf("Usage:%s <IP> <port>\n", argv[0]);

               exit(1);

        }

 

        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

               ErrorHandling("WSAStartup() error!");

 

        hSocket = socket(PF_INET, SOCK_STREAM, 0); //socket 함수호출을 통해서 TCP소켓을 생성

        if (hSocket == INVALID_SOCKET)

               ErrorHandling("hSocket() error!");

 

        memset(&servAddr, 0, sizeof(servAddr));

        servAddr.sin_family = AF_INET;

        servAddr.sin_addr.s_addr = inet_addr(argv[1]);

        servAddr.sin_port = htons(atoi(argv[2]));

 

        if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)

               ErrorHandling("connect() error!");

 

        for (i = 0; i < 100; i++) //서버가 데이터를 모두 전송시킬때까지 기다려준다

               printf("Wait time %d\n", i);

 

        recv(hSocket, message, sizeof(message) - 1, 0); //전송 받은 데이터를 읽어온다

        printf("Message from server: %s\n", message);

 

        closesocket(hSocket);

        WSACleanup();

        return 0;

}

 

void ErrorHandling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

서버가 데이터를 모두 전송할 때까지 충분히 시간을 준 다음 서버로부터 데이터를 받고 출력한다. Hello World!


[참고] 윤성우 저 TCP/IP 소켓 프로그래밍

반응형