Study/Network

[열혈 TCP/IP 소켓 프로그래밍] Chapter2. 내용 확인문제

게임 만드는 나무꾼 2022. 9. 19. 19:50

01. 프로토콜이 무엇인가?

  • 프로토콜이란, 데이터의 전송 방식을 결정하는 규약이다.
  • 두 컴퓨터 간 프로토콜을 정의해야 데이터 전송 방식을 서로 알고 해석할 수 있다.

02. 연결지향 소켓 TCP의 전송 특성 3가지

  • 데이터의 손실이 없음이 보장된다.
  • 데이터의 경계가 없다
  • 데이터의 전송 순서가 보장된다.

03. 비연결지향 소켓 UDP의 전송 특성

  • 데이터의 손실이 있을 수 있다.
  • 빠른 전송을 목표로 한다.
  • 한 번의 전송에 데이터의 크기가 제한된다.
  • 연결이라는 개념이 존재하지 않는다.

04. 유형별 적합한 타입의 소켓은?

  • 실시간 스트리밍 : UDP 소켓. 빠른 전송이 목표가 되어야 하고, 데이터가 손실되더라도 최신의 데이터를 계속 받는 것이 더 중요하다.
  • 텍스트 파일 전송 : TCP 소켓. 순서가 무조건 보장되어야 하고 데이터의 손실이 없어야 한다.
  • 인터넷 뱅킹 : 데이터의 손실이 절대 있어서는 안 된다.

05. 데이터의 경계가 존재하지 않는 소켓은? 그리고 이러한 소켓을 통해 데이터를 수신할 때 주의점

  • 데이터의 경계가 존재하지 않는 소켓은 TCP 소켓이다.
  • 수신할 때 주의할 점은, 소켓 내부의 버퍼에 데이터가 저장되고, read 함수 호출을 통해 버퍼에 있는 데이터를 비우는 방식이기 때문에 버퍼가 가득 차게 된다면 더 이상 데이터를 수신할 수 없다.
  • 데이터가 손실되지는 않지만 데이터를 더 이상 받을 수 없기 때문에 주의해야 한다.

06. 서버에서 데이터를 쪼개서 보내보기

서버 코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

void ErrorHandling(char* Message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hServerSock, 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");
	}

	hServerSock = socket(PF_INET, SOCK_STREAM, 0);
	if (hServerSock == 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(hServerSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
	{
		ErrorHandling("bind() error");
	}

	if (listen(hServerSock, 5) == SOCKET_ERROR)
	{
		ErrorHandling("listen() error");
	}

	szClntAddr = sizeof(clntAddr);
	hClntSock = accept(hServerSock, (SOCKADDR*)&clntAddr, &szClntAddr);

	if (hClntSock == INVALID_SOCKET)
	{
		ErrorHandling("accept() error");
	}

	int len = strlen(message);

	for (int i = 0; i < len; ++i)
	{
		send(hClntSock, &message[i], sizeof(char), 0);
	}

	closesocket(hClntSock);
	closesocket(hServerSock);
	WSACleanup();
}

void ErrorHandling(char* Message)
{
	fputs(Message, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

클라이언트 코드

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

void ErrorHandling(char* Message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hSocket;
	SOCKADDR_IN servAddr;

	char message[30];

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

	if (hSocket == INVALID_SOCKET)
	{
		ErrorHandling("socket() 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");
	}

	// busy - waiting
	for (int i = 0; i < 3000; ++i)
	{
		printf("Wait Time : %d\n", i);
	}

	recv(hSocket, message, 12, 0);
    message[12] = '\0';

	printf("Message From Server : %s", message);

	closesocket(hSocket);
	WSACleanup();

	return 0;
}

void ErrorHandling(char* Message)
{
	fputs(Message, stderr);
	fputc('\n', stderr);
	exit(1);
}