251001

凡愚·2025년 10월 1일

개발 일지

목록 보기
311/350

✅ 한 것들


  • 윤성우의 열혈 TCP/IP 소켓 프로그래밍
  • 코딩 인터뷰 완전 분석
  • Project FDS


📖 윤성우의 열혈 TCP/IP 소켓 프로그래밍


계산기 프로토콜

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 1024
void ErrorHandling(char *message);
int SumAll(int *msgBuf, int recvCount)
{
	int result = 0;
	for(int i=0; i<recvCount; i++) result += msgBuf[i];
	return result;
}

int SubtractAll(int *msgBuf, int recvCount)
{
	if(recvCount == 0) return 0;
	int result = msgBuf[0];
	for(int i=1; i<recvCount; i++) result -= msgBuf[i];
	return result;
}

int MultiplyAll(int *msgBuf, int recvCount)
{
	if(recvCount == 0) return 0;
	int result = 1;
	for(int i=0; i<recvCount; i++) result *= msgBuf[i];
	return result;
}

int main(int argc, char *argv[])
{
	WSADATA wsaData;
	SOCKET hServSock, hClntSock;
	char message[BUF_SIZE];
	int msgBuf[BUF_SIZE];
	int strLen, i, j, recvCount;

	SOCKADDR_IN servAdr, clntAdr;
	int clntAdrSize;

	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(&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(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
		ErrorHandling("bind() error");

	if(listen(hServSock, 5)==SOCKET_ERROR)
		ErrorHandling("listen() error");

	clntAdrSize=sizeof(clntAdr);

	for(i=0; i<5; i++)
	{
		hClntSock=accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize);
		if(hClntSock==-1)
			ErrorHandling("accept() error");
		else
			printf("Connected client %d \n", i+1);

		recvCount = 0;
		while(1)
		{
			int msgLen = recv(hClntSock, message, BUF_SIZE, 0);
			printf("msgLen : %d\n", msgLen);
			char *s = message;
			while(*s==' '||*s=='\t'||*s=='\r'||*s=='\n') s++;
			char *e = s + strlen(s);
			while(e>s && (e[-1]==' '||e[-1]=='\t'||e[-1]=='\r'||e[-1]=='\n')) e--;
			*e = '\0';

			if(strcmp(message,"+") == 0 ||strcmp(message,"-") == 0 || strcmp(message,"*") == 0) { break; }
			else
			{
				msgBuf[recvCount] = atoi(message);
				recvCount++;
				send(hClntSock,message,strlen(message),0);
			}
		}

		int result = 0;
		if(strcmp(message,"+") == 0) result = SumAll(msgBuf, recvCount);
		else if (strcmp(message,"-") == 0) result = SubtractAll(msgBuf, recvCount);
     	else if (strcmp(message,"*") == 0) result = MultiplyAll(msgBuf, recvCount);

		char resStr[10];
		itoa(result,resStr,10);
		printf("send result\n");
		send(hClntSock, resStr, strlen(resStr), 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 <string.h>
#include <winsock2.h>

#define BUF_SIZE 1024
void ErrorHandling(char *message);

int main(int argc, char *argv[])
{
	WSADATA wsaData;
	SOCKET hSocket;
	char message[BUF_SIZE];
	int strLen;
	SOCKADDR_IN servAdr;

	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(&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]));

	if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
		ErrorHandling("Connect() error!");
	else
		puts("Connected........");

	while(1)
	{
		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);
		strLen = recv(hSocket, message, BUF_SIZE-1, 0);
		message[strLen] = 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);
}

계산기 클라

책은 클라에서 미리 연산자까지의 숫자 개수를 보내주는 식으로 구현함.

05-2 TCP의 이론적인 이야기

TCP 소켓 입출력 버퍼

read()로 읽기 전의 데이터들은 입력 버퍼에 대기한다.
write()를 호출하면 데이터 수신 전 출력 버퍼에 대기한다.

  • 입출력 버퍼는 TCP 소켓 각각에 대해 별도로 존재하며, 소켓 생성시 자동 생성된다
  • 소켓 닫아도 출력 버퍼 데이터(write()한거)는 계속해서 전송된다
  • 소켓 닫으면 입력 버퍼(read()한거)는 소멸된다

Q. 입력 버퍼의 크기를 넘어서는 데이터가 전송되면?
TCP에는 슬라이딩 윈도우라는 프로토콜이 존재한다.
이것은 입력 버퍼의 크기를 초과하는 데이터 전송을 막아준다.

TCP 내부 동작원리

상대 소켓과의 연결

  • SYN → SYN + ACK → ACK
  • SEQ: 2000, ACK : 1001 같은 식으로 패킷에 번호를 붙인다
    • 이 때문에 손실 데이터 확인 및 재전송 가능

상대 소켓과의 데이터 송수신

  • SEQ + data → ACK → (반복)
  • SEQ 1200, 100 byte dataACK 1301 (패킷 번호 1200에 데이터 크기 100 + 1) → SEQ 1301 data ...

상대 소켓과의 연결 종료

  • FIN(연결 끊겠습니다) → ACK(알겠습니다 잠시만요) ... FIN(저도 연결 끊겠습니다) → ACK(확인했습니다)


📖 코딩 인터뷰 완전 분석


p.40 ~ 58



🎮 Project FDS


Project GNC 레포 이어받아서 Project FDS

  • 깃허브 및 유니티 프로젝트 이름 변경


0개의 댓글