ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [열혈 TCP/IP 소켓 프로그래밍] Chapter4. 내용 확인문제
    Study/Network 2022. 10. 8. 17:45

    01. TCP/IP 프로토콜 스택을 4계층으로 구분하고, TCP소켓이 거치는 계층구조와 UDP 소켓이 거치는 계층구조의 차이점 설명

    • TCP/IP 프로토콜 스택은 LINK - IP - TCP/UDP - APPLICATION 계층으로 구분되어 있다.
    • TCP 소켓은 TCP 스택을 거치고, UDP 소켓은 UDP 스택을 거친다. (당연한듯;;)

    02. LINK계층과 IP계층이 담당하는 역할, 관계

    • LINK 계층은 물리적인 연결에 대한 프로토콜을 정의한다.
    • IP 계층은 데이터의 송수신 경로에 대한 프로토콜을 정의한다. (IP계층에서는 송수신의 오류발생에 대한 대비가 되어있지 않다.)
    • IP계층은 LINK계층의 물리적 연결 프로토콜 위에서 동작한다

    03. TCP/IP 프로토콜 스택이 4가지로 나뉘는 이유를 개방형 시스템과 연결지어 설명

    • 네트워크는 물리적 연결, 데이터 송수신, 경로 선택 등 복잡한 구조이다.
    • 각 문제를 영역별로 쪼개어 해결함으로서 유지/보수가 용이해진다.
    • 또한, 각 계층에 대한 표준이 정해짐(개방형 시스템)으로서 하드웨어, 소프트웨어적 관점에서의 호환성이 증대된다. 이를 통해 빠른 기술 발전이 이루어질 수 있었다.

    04. 클라이언트는 서버의 어떤 함수 호출 이후부터 connect() 함수를 호출할 수 있는가?

    • 서버가 listen() 함수를 호출한 이후부터

    05. 연결 요청 대기 큐가 생성되는 순간은 언제이고, 무슨 역할을 하는가? accept 함수와의 관계는?

    • 서버에서 listen()함수를 호출하면서 연결 요청 대기 큐가 생성된다.
    • 연결 요청 대기 큐는 클라이언트가 connect() 함수를 통해 서버와의 연결을 요청하였을 때 클라이언트의 연결 요청을 받아들이고, 대기시키는 역할을 한다.
    • 이후, 서버에서 accept()함수를 호출함으로서 연결 요청 대기 큐에서 연결 요청 하나를 가져와 데이터를 송수신한다.

    06. 클라이언트에서 bind함수호출이 불필요한 이유와, IP주소와 PORT 번호가 할당되는 시점?

    • connect함수를 호출했을 때, 운영체제(커널)에서 자동으로 할당하기 때문이다.
    • IP는 컴퓨터의 IP주소, PORT번호는 운영체제에서 무작위로 할당한다.

    07. Hello World 예제 Iterative Server/Client구조로 변경해보기

    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #include <cstdio>
    #include <cstdlib>
    #include <winsock.h>
    
    #pragma comment(lib, "ws2_32.lib")
    
    #define QUEUE_SIZE 5
    #define BUF_SIZE 1024
    
    const char* serverMessage = "Hello World!\n";
    
    void main(int argc, char* argv[])
    {
    	WSADATA wsaData = {};
    
    	SOCKET hServSock, hClntSock;
    
    	SOCKADDR_IN	servAddr, clntAddr;
    	int clntAddrSize;
    
    	if (argc != 2)
    	{
    		printf("Usage : %s <port>\n", argv[0]);
    		exit(1);
    	}
    
    	// WSA library 2.2 버전으로 초기화
    	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	{
    		printf("WSAStartUp() Error");
    		exit(1);
    	}
    
    	// IPv4, TCP 소켓 생성
    	hServSock = socket(PF_INET, SOCK_STREAM, 0);
    
    	if (hServSock == INVALID_SOCKET)
    	{
    		printf("socket() Error");
    		exit(1);
    	}
    
    	// 주소 정보 초기화
    	memset(&servAddr, 0, sizeof(SOCKADDR_IN));
    	servAddr.sin_family = AF_INET; // IPv4기반의 주소 체계 사용
    	servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 현재 컴퓨터의 IP주소를 IP주소로 사용, host to Network Byte Order
    	servAddr.sin_port = htons(atoi(argv[1]));
    
    	// IP, PORT 할당
    	if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
    	{
    		printf("bind() Error");
    		exit(1);
    	}
    
    	// 연결 요청 대기상태 진입 (연결 요청 대기 큐 size : 5)
    	if (listen(hServSock, QUEUE_SIZE) == SOCKET_ERROR)
    	{
    		printf("listen() Error");
    		exit(1);
    	}
    
    	clntAddrSize = sizeof(clntAddr);
    
    	int clntStrLen = 0;
    	char clntMessage[BUF_SIZE] = {};
    
    	// 최대 5개의 클라이언트 요청까지 처리
    	for (int i = 0; i < QUEUE_SIZE; ++i)
    	{
    		// 연결 요청 수락
    		hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &clntAddrSize);
    
    		if (hClntSock == INVALID_SOCKET)
    		{
    			printf("accept() Error");
    			exit(1);
    		}
    		else
    		{
    			printf("Client [%d] Connected.\n", i + 1);
    		}
    
    		do
    		{
    			// 수신 데이터가 있을 때까지 대기
    			clntStrLen = recv(hClntSock, clntMessage, BUF_SIZE, 0);
    
    			if (clntStrLen == 0)
    				break;
    
    			// Hello World 송신
    			send(hClntSock, serverMessage, strlen(serverMessage), 0);
    		} while (clntStrLen != 0);
    
    		closesocket(hClntSock);
    	}
    
    	closesocket(hServSock);
    
    	printf("Close Server.\n");
    	WSACleanup();
    }

    서버 코드

    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    
    #include <cstdio>
    #include <cstdlib>
    #include <winsock.h>
    
    #pragma comment(lib, "ws2_32.lib")
    
    #define BUF_SIZE 1024
    
    void main(int argc, char* argv[])
    {
    	WSADATA wsaData = {};
    	SOCKET hSocket;
    
    	if (argc != 3)
    	{
    		printf("Usage : %s <IP> <PORT>", argv[0]);
    		exit(1);
    	}
    
    	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	{
    		printf("WSAStartUp() Error");
    		exit(1);
    	}
    
    	hSocket = socket(PF_INET, SOCK_STREAM, 0);
    
    	if (hSocket == INVALID_SOCKET)
    	{
    		printf("socket() Error");
    		exit(1);
    	}
    
    	SOCKADDR_IN servAddr = {};
    	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)
    	{
    		printf("connect() Error");
    		exit(1);
    	}
    	else
    	{
    		printf("Connected..................\n");
    	}
    
    	char message[BUF_SIZE] = {};
    	int recvLen = 0;
    	while (true)
    	{
    		fputs("Input Message (Q to Quit) : ", stdout);
    		fgets(message, BUF_SIZE, stdin);
    
    		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
    		{
    			break;
    		}
    
    		send(hSocket, message, strlen(message), 0);
    
    		recvLen = recv(hSocket, &message[0], BUF_SIZE - 1, 0);
    		message[recvLen] = '\0';
    		printf("Message From Server : %s", message);
    	}
    
    	closesocket(hSocket);
    	printf("Quit Client.\n");
    }

    클라이언트 코드

    댓글

GameDev.