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

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

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

1. TCP보다 UDP가 빠른 이유는 무엇인가? 그리고 TCP는 데이터의 전송을 신뢰할 수 있지만 UDP는 신뢰할 수 없는 이유는 또 무엇인가?


>UDP는 TCP와 달리 흐름제어를 하지 않기 때문에 TCP보다 빠르다. 이 흐름제어는 소켓의 연결과 종료, 그리고 데이터 송수신 전반에 걸쳐서 진행되기 때문에 TCP의 데이터 전송은 언제나 신뢰할 수 있다. 하지만, 이 흐름제어 때문에 속도는 UDP보다 느리다.


2. 다음 중 UDP의 특성인 아닌 것을 모두 고르면?


>b. UDP 기반으로 데이터를 전송할 목적지가 두 군데라면, 총 두개의 소켓을 생성해야 한다

  ->UDP 소켓이 하나 있다면 어디건 데이터를 전송할 수 있다.


  c. UDP 소켓은 TCP 소켓이 할당한 동일한 번호의 PORT에 재할당이 불가능하다.

  ->재할당이 가능하다.


  e. UDP 소켓을 대상으로도 connect 함수를 호출할 수 있는데, 이러한 경우 UDP 소켓도 TCP 소켓과 마찬가지로 Three-way handshaking 과정을 거치게 된다

  ->UDP 소켓을 대상으로 connect 함수를 호출했다고 해서 목적지의 UDP 소켓과 연결설정 과정을 거친다거나 하지는 않는다.


*UDP의 특성인 것


  a. UDP는 TCP와 달리 연결의 개념이 존재하지 않는다. 따라서 반드시 TCP에서 보인 것처럼 1대 1의 형태로 데이터를 송수신 하지 않는 것이다


  d. UDP 소켓과 TCP 소켓은 공존할 수 있다. 따라서 필요하다면 한 호스트 상에서 TCP 방식과 UDP 방식의 데이터 송수신을 모두 진행할 수 있다.


3. UDP 데이터그램이 최종 목적지인 상대 호스트의 UDP 소켓에 전달되는데 있어서 IP가 담당하는 부분과 UDP가 담당하는 부분을 구분지어 설명해보자


>IP는 목적지 호스트로의 데이터 전달을 담당한다. 반면 UDP는 목적지 호스트에 전달된 데이터의 PORT번호를 확인해서 최종 목적지인 소켓으로의 데이터 전달을 담당한다


4. UDP는 일반적으로 TCP보다 빠르다. 그러나 송수신하는 데이터의 성격에 따라서 그 차이는 미미할 수도 있고, 반대로 매우 클 수도 있다. 그렇다면 어떠한 상황에서 UDP는 TCP보다 매우 좋은 성능을 보이는지 설명해보자


>UDP는 TCP와 달리 두 소켓간의 연결, 연결해제과정을 거치지 않는다. 따라서 매우 비번한 연결, 연결해제의 과정이 요구되는 경우 TCP 기반의 데이터 송수신보다 UDP 기반의 데이터 송수신이 더 빠른 성능을 보인다.


*한번에 실제 송수신하는 데이터의 양은 적지만 매우 빈번한 데이터의 송수신이 요구되어서 TCP 기반에서 구현할 경우 빈번한 연결, 연결해제의 과정을 거쳐야 하는 경우 UDP가 TCP보다 매우 좋은 성능을 보인다!


5. 클라이언트의 TCP 소켓은 connect 함수를 호출할 때 자동으로 IP와 PORT가 할당된다. 그렇다면 bind 함수를 호출하지 않는 UDP 소켓은 언제 IP와 PORT가 할당되는가?


>sendto 함수를 호출할 때까지 주소정보가 할당되지 않았다면, sendto 함수가 처음으로 호출되었을 때 해당 소켓에 IP와 PORT 번호가 자동으로 할당된다. 또한, 이렇게 한번 할당하면 프로그램이 종료될 때까지 주소정보가 그대로 유지되기 때문에 다른 UDP 소켓과 데이터를 주고 받을 수 있다. 물론 IP는 호스트의 IP로, PORT는 사용되지 않고 있는 PORT 번호 중 하나를 임의로 골라서 할당된다.


6. connect 함수의 호출문장은 TCP 클라이언트의 경우 반드시 삽입해야 하는 문장이다. 그러나 UDP의 경우는 선택적으로 삽입이 가능하다. 그렇다면 UDP에서 connect 함수를 호출하면 어떠한 이점이 있는가?


>UDP 소켓을 대상으로 sendto 함수를 호출 했을 때 다음의 세 과정을 거친다

  1. UDP 소켓에 목적지의 IP와 PORT번호 등록

  2. 데이터 전송

  3. UDP에 등록된 목적지 정보의 삭제 


  이 중에서 데이터를 전송할 때마다 반복되는 1단계와 3단계 과정을 connect 함수를 통해 생략이 가능하다. connect 함수를 호출한다고 해서 연결의 과정을 거치는 것은 아니며, IP와 PORT번호를 UDP 소켓에 지정하기만 한다. 그리고 이렇게 connect 함수 호출 후에는 sendto, recvto 함수가 아닌, write, read 함수의 호출로도 데이터를 송수신할 수 있다.


7. 본문에서 보인 예제 uecho_server.c와 uecho_client.c를 참고해서 서버와 클라이언트 상호간에 한번씩 메세지를 주고받는 형태와 대화를 진행하는 예제를 작성해보자. 단, 주고받는 대화는 콘솔상에 출력되어야 한다.


>uecho_server.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

 

#define BUF_SIZE 30

void error_handling(char *message);

 

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

{

        int serv_sock;

        char message[BUF_SIZE];

        int str_len;

        socklen_t clnt_adr_sz;

 

        struct sockaddr_in serv_adr, clnt_adr;

        if (argc != 2)

        {

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

               exit(1);

        }

 

        serv_sock = socket(PF_INET, SOCK_DGRAM, 0); //UDP 소켓의 생성을 위해서 socket 함수의 번째 인자로 SOCK_DGRAM 전달하고 있다

        if (serv_sock == -1)

               error_handling("UDP socket creation error");

 

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

        serv_adr.sin_family = AF_INET;

        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);

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

 

        if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)

               error_handling("bind() error");

 

        while (1)

        {

               clnt_adr_sz = sizeof(clnt_adr);

               str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); //할당된 주소로 전달되는 모든 데이터를 수신하고 있다. 물론 데이터의 전달대상에는 제한이 없다

               sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz); //함수호출을 통해서 데이터를 전송한 이의 주소정보도 함께 얻게 되는데 바로 주소정보를 이용해서 수신된 데이터를 역으로 재전송한다

        }

        close(serv_sock); //while문이 무한루프이고, 루프를 빠져나가기 위한 break 문이 삽입되지 않았기 때문에 사실상 문장은 실행되지 않는다

        return 0;

}

 

void error_handling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

uecho_client.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

 

#define BUF_SIZE 330

 

void error_handling(char *message);

 

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

{

        int sock;

        char message[BUF_SIZE];

        int str_len;

        socklen_t adr_sz;

 

        struct sockaddr_in serv_adr, from_adr;

        if (argc != 3)

        {

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

               exit(1);

        }

 

        sock = socket(PF_INET, SOCK_DGRAM, 0); //UDP 소켓을 생성하였다. 이제 남은 것은 데이터 송수신 함수의 호출뿐이다

        if (sock == -1)

               error_handling("socket() error");

 

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

        serv_adr.sin_family = AF_INET;

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

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

 

        while (1)

        {

               fputs("Insert message(q to quit): ", stdout);

               fgets(message, sizeof(message), stdin);

               if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

                       break;

 

               sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr)); //서버로 데이터를 전송하고

               adr_sz = sizeof(from_adr);

               str_len = recvffrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); //전송되어오는 데이터를 수신하고 있다

               message[str_len] = 0;

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

        }

        close(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/socket.h>

 

#define BUF_SIZE 30

void error_handling(char *message);

 

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

{

        int serv_sock;

        char message[BUF_SIZE];

        int str_len;

        socklen_t clnt_adr_sz;

 

        struct sockaddr_in serv_adr, clnt_adr;

        if (argc != 2) 

   {

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

               exit(1);

        }

 

        serv_sock = socket(PF_INET, SOCK_DGRAM, 0);//UDP 소켓의 생성을 위해서 socket 함수의 번째 인자로 SOCK_DGRAM 전달하고 있다

        if (serv_sock == -1)

               error_handling("UDP socket creation error");

 

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

        serv_adr.sin_family = AF_INET;

        serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);

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

 

        if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)

               error_handling("bind() error");

 

        clnt_adr_sz = sizeof(clnt_adr);

        while (1)

        {

               str_len = recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); //할당된 주소로 전달되는 모든 데이터를 수신하고 있다. 물론 데이터의 전달대상에는 제한이 없다

               message[str_len] = 0;

               printf("Message from client: %s", message);

 

               fputs("Insert message(q to quit): ", stdout);

               fgets(message, sizeof(message), stdin);

               if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

                       break;

               //함수호출을 통해서 데이터를 전송한 이의 주소정보도 함께 얻게 되는데 바로 주소정보를 이용해서데이터를 전송한다

               sendto(serv_sock, message, strlen(message), 0,(struct sockaddr*)&clnt_adr, clnt_adr_sz);

        }

        close(serv_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/socket.h>

 

#define BUF_SIZE 30

void error_handling(char *message);

 

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

{

        int sock;

        char message[BUF_SIZE];

        int str_len;

        socklen_t adr_sz;

 

        struct sockaddr_in serv_adr, from_adr;

        if (argc != 3) 

   {

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

               exit(1);

        }

 

        sock = socket(PF_INET, SOCK_DGRAM, 0);//UDP 소켓을 생성하였다. 이제 남은 것은 데이터 송수신 함수의 호출뿐이다

        if (sock == -1)

               error_handling("socket() error");

 

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

        serv_adr.sin_family = AF_INET;

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

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

 

        while (1)

        {

               fputs("Insert message(q to quit): ", stdout);

               fgets(message, sizeof(message), stdin);

               if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

                       break;

 

               sendto(sock, message, strlen(message), 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr));//서버로 데이터를 전송하고

               adr_sz = sizeof(from_adr);

               str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz);//전송되어오는 데이터를 수신하고 있다

 

               message[str_len] = 0;

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

        }

        close(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 <WinSock2.h>

 

#define BUF_SIZE 30

 

void ErrorHandling(char *message);

 

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

{

        WSADATA wsaData;

        SOCKET servSock;

        char message[BUF_SIZE];

        int strLen;

        int clntAdrSz;

 

        SOCKADDR_IN servAdr, clntAdr;

        if (argc != 2)

        {

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

               exit(1);

        }

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

               ErrorHandling("WSAStartup() error!");

 

        servSock = socket(PF_INET, SOCK_DGRAM, 0); //UDP 소켓 생성을 위해서 socket 함수의 두번째 인자로 SOCK_DGRAM 전달

        if (servSock == INVALID_SOCKET)

               ErrorHandling("UDP socket creation error!");

 

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

        servAdr.sin_family = AF_INET;

        servAdr.sin_addr.s_addr = htonl(INADDR_ANY);

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

 

        if (bind(servSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)

               ErrorHandling("bind() error");

 

        clntAdrSz = sizeof(clntAdr);

        while (1)

        {

               strLen = recvfrom(servSock, message, BUF_SIZE, 0, (SOCKADDR*)&clntAdr, &clntAdrSz); //할당된 주소로 전달되는 모든 데이터를 수신, 물론 데이터의 전달대상에는 제한이 없다

               message[strLen] = 0;

               printf("Message from client:%s", message);

 

               fputs("Insert message(q to quit): ", stdout);

               fgets(message, sizeof(message), stdin);

               if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

                       break;

               sendto(servSock, message, strLen, 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr)); //함수호출을 통해서 데이터를전송한 이의 주소정보도 함께 얻게 되는데, 바로 주소정보를 이용해서 수신된 데이터를 역으로 재전송

        }

        closesocket(servSock);

        WSACleanup();

        return 0;

}

 

void ErrorHandling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

마찬가지로 정상적으로 작동하면 cmd 창이 멈추고 커서만 깜빡인다


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

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <WinSock2.h>

 

#define BUF_SIZE 30

 

void ErrorHandling(char *message);

 

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

{

        WSADATA wsaData;

        SOCKET sock;

        char message[BUF_SIZE];

        int strLen, adrSize;

 

        SOCKADDR_IN servAdr, clntAdr;

        if (argc != 3)

        {

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

               exit(1);

        }

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

               ErrorHandling("WSAStartup() error!");

 

        sock = socket(PF_INET, SOCK_DGRAM, 0); //UDP 소켓을 생성, 이제 남은 것은 데이터 송수신 함수의 호출

        if (sock == INVALID_SOCKET)

               ErrorHandling("socket() error");

 

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

        servAdr.sin_family = AF_INET;

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

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

 

        while (1)

        {

               fputs("Insert message(q to quit): ", stdout);

               fgets(message, sizeof(message), stdin);

               if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

                       break;

               sendto(sock, message, strlen(message), 0,(struct sockaddr*)&servAdr, sizeof(servAdr)); //보내고

               adrSize = sizeof(clntAdr);

               strLen = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&clntAdr, &adrSize); //받는다

 

               message[strLen] = 0;

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

        }

        closesocket(sock);

        WSACleanup();

        return 0;

}

 

void ErrorHandling(char *message)

{

        fputs(message, stderr);

        fputc('\n', stderr);

        exit(1);

}

 

윈도우에서도 서버와 클라이언트간에 간단한 채팅이 가능하다는 것을 확인할 수 있다!


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

반응형