[C++] TCP Server

seunghyunΒ·2023λ…„ 7μ›” 5일
0

Server

λͺ©λ‘ 보기
3/4

TCP πŸ†š UDP

TCP, UDP λŠ” 전화와 이메일 차이라고 λΉ„μœ ν•  수 μžˆλ‹€.

연결지ν–₯μ„±

TCPλŠ” μ—°κ²°ν˜• μ„œλΉ„μŠ€, UDPλŠ” λΉ„μ—°κ²°ν˜• μ„œλΉ„μŠ€λΌ ν•œλ‹€.

TCP λŠ” 연결을 μœ„ν•΄ ν• λ‹Ήλ˜λŠ” 논리적인 κ²½λ‘œκ°€ μžˆλ‹€.

  • λ˜ν•œ 전솑 μˆœμ„œκ°€ 보μž₯λœλ‹€.

UDP λŠ” μ—°κ²°μ΄λΌλŠ” κ°œλ…μ΄ μ—†κ³  전솑 μˆœμ„œκ°€ 보μž₯λ˜μ§€ μ•ŠλŠ”λ‹€.

  • λ˜ν•œ 경계(Boundary)의 κ°œλ…μ΄ μžˆλ‹€.
  • TCP 처럼 μ—°κ²°λ˜λŠ” κ°œλ…μ΄ μ•„λ‹ˆλΌμ„œ accept, connect 의 κ°œλ…μ΄ μ—†λ‹€?

속도와 μ‹ λ’°μ„±

TCPλŠ” 신뒰성은 μ’‹μ§€λ§Œ 속도가 (UDP에 λΉ„ν•΄ μƒλŒ€μ μœΌλ‘œ)λŠλ¦¬λ‹€.

  • 뢄싀이 μΌμ–΄λ‚˜λ©΄ μ±…μž„μ§€κ³  λ‹€μ‹œ μ „μ†‘ν•œλ‹€.
  • 물건을 μ£Όκ³  받을 상황이 μ•„λ‹ˆλ©΄ μΌλΆ€λ§Œ 보낸닀. (흐름/ν˜Όμž‘μ œμ–΄β­οΈ)
  • κ³ λ €ν•  것이 λ§Žμ•„μ„œ 속도가 λŠλ¦¬λ‹€.
  • λŠλ¦¬λ”λΌλ„ λ§Žμ€ μ‚¬λžŒμ„ κ³΅ν‰ν•˜κ²Œ

UDPλŠ” 신뒰성은 λ‚˜μ˜μ§€λ§Œ 속도가 λΉ λ₯΄λ‹€.

  • 뢄싀에 λŒ€ν•΄λ‚˜ μ±…μž„μ΄ μ—†μ–΄μ„œ λ„€νŠΈμ›Œν¬ 훼손 문제, λ‚­λΉ„κ°€ μ‹¬ν•΄μ§ˆ 수 μžˆλ‹€
  • 일단 보내고 μƒκ°ν•œλ‹€.
  • λ‹¨μˆœν•΄μ„œ 속도가 λΉ λ₯΄λ‹€.
  • λΉ λ₯΄κ²Œ ν•œλ‘λͺ…

데이터 경계 Boundary

TCPλŠ” κ²½κ³„μ˜ κ°œλ…μ΄ μ—†λ‹€ (μˆœμ„œλŠ” 보μž₯λ˜μ§€λ§Œ 경계가 μ—†λŠ”, 컨베이어 벨트 λŠλ‚Œ)

UDPλŠ” κ²½κ³„μ˜ κ°œλ…μ΄ μžˆλ‹€ (μˆœμ„œλŠ” 보μž₯λ˜μ§€ μ•Šμ§€λ§Œ 택배 포μž₯처럼 경계가 λ”±λ”± μžˆλ‹€)


TCP Server

winsock 라이브러리λ₯Ό ν™œμš©ν•˜μ—¬ 맀우 기본적인 ν˜•νƒœμ˜ TCP μ„œλ²„λ₯Ό κ΅¬ν˜„ν•΄λ³Έλ‹€. blocking ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ 둜컬 루프백 μ£Όμ†Œλ‘œ 에코 μ±„νŒ…μ„ ν•΄λ³΄μž

κ°„λ‹¨ν•˜κ²Œ 정리해보면 이런 ν˜•νƒœμ΄λ‹€

ν΄λΌμ΄μ–ΈνŠΈ
1) μ†ŒμΌ“ 생성 (socket)
2) μ„œλ²„μ— μ—°κ²° μš”μ²­ (connect)
3) 톡신 (send, receive)

μ„œλ²„
1) μƒˆλ‘œμš΄ μ†ŒμΌ“ 생성 (socket)
2) μ†ŒμΌ“μ— μ£Όμ†Œ/포트 번호 μ„€μ • (bind)
3) μ†ŒμΌ“ 일 μ‹œν‚€κΈ° (listen)
4) μ†λ‹˜ 접속 (accept)
5) 클라와 톡신 (send, receive)

Dummy Client

#include "pch.h"
#include <iostream>

// 클라
// 1) μƒˆλ‘œμš΄ μ†ŒμΌ“ 생성
// 2) μ„œλ²„μ— μ—°κ²° μš”μ²­
// 3) μ„œλ²„μ™€ 톡신
int main()
{
	WSAData wsaData;
	if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		return 0;

	// ad : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
	// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
	// protocol : 0
	// return : descriptor
	SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (clientSocket == INVALID_SOCKET)
		return 0;

	SOCKADDR_IN serverAddr; // IPv4
	::memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	//serverAddr.sin_addr.s_addr = ::inet_addr("127.0.0.1"); << deprecated
	::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
	serverAddr.sin_port = ::htons(7777); // 80 : HTTP

	if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
		return 0;

	// ---------------------------
	// μ—°κ²° 성곡! μ΄μ œλΆ€ν„° 데이터 μ†‘μˆ˜μ‹  κ°€λŠ₯!
	cout << "Connected To Server!" << endl;

	while (true)
	{
		// TODO

		char sendBuffer[100] = "Hello! I am Client";
		int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
		if (resultCode == SOCKET_ERROR)
			return 0;

		char recvBuffer[100];
		int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
		if (recvLen <= 0)
			return 0;

		cout << "Echo Data : " << recvBuffer << endl;

		this_thread::sleep_for(1s);
	}

	// --------------------------

	::closesocket(clientSocket);
	::WSACleanup();
}

Server

#include "pch.h"
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
#include <atomic>
#include <mutex>
#include <windows.h>
#include "TestMain.h"
#include "ThreadManager.h"


// μ„œλ²„
// 1) μƒˆλ‘œμš΄ μ†ŒμΌ“ 생성 (socket)
// 2) μ†ŒμΌ“μ— μ£Όμ†Œ/포트 번호 μ„€μ • (bind)
// 3) 리슨 μ†ŒμΌ“ 일 μ‹œν‚€κΈ° (listen)
// 4) μ ‘μ†λœ 클라에 λŒ€ν•΄μ„œ μƒˆλ‘œμš΄ μ†ŒμΌ“μ„ 생성 (accept)
// 5) 클라와 톡신

int main()
{
	WSADATA wsaData;
	if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		return 0;

	// 1) μ†ŒμΌ“ 생성
	// ad : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
	// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
	// protocol : 0
	// return : descriptor
	//int32 errorCode = ::WSAGetLastError();
	SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (listenSocket == 0)
		return 0;

	// 2) μ£Όμ†Œ/포트 번호 μ„€μ • (bind)
	// μ—°κ²°ν•  λͺ©μ μ§€λŠ”? (IPμ£Όμ†Œ + Port) -> XX μ•„νŒŒνŠΈ YY 호
	SOCKADDR_IN serverAddr; // IPv4
	::memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); // 아무 IPλ‚˜
	serverAddr.sin_port = ::htons(7777); // 80 : HTTP

	// host to network short
	// Little-Endian vs Big-Endian
	// ex) 0x12345678 4λ°”μ΄νŠΈ μ •μˆ˜
	// low [0x78][0x56][0x34][0x12] high < little
	// low [0x12][0x34][0x56][0x78] high < big = network

	if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
		return 0;

	// 3) 업무 κ°œμ‹œ (listen)
	if (::listen(listenSocket, SOMAXCONN) == SOCKET_ERROR)
		return 0;

	// 4) μ†λ‹˜ 맞이 (accept)
	while (true)
	{
		SOCKADDR_IN clientAddr;
		::memset(&clientAddr, 0, sizeof(clientAddr));
		int32 addrLen = sizeof(clientAddr);
		
		SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
		if (clientSocket == INVALID_SOCKET)
			return 0;

		// μ†λ‹˜ μž…μž₯!
		char ip[16];
		::inet_ntop(AF_INET, &clientAddr.sin_addr, ip, sizeof(ip));
		cout << "Client Connected! IP = " << ip << endl;

		// 5) TODO
		while (true)
		{
			char recvBuffer[100];
			int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
			if (recvLen <= 0)
				return 0;

			cout << "Recv Data : " << recvBuffer << endl;
			cout << "Recv Data Len : " << recvLen << endl;

			int32 resultCode = ::send(clientSocket, recvBuffer, recvLen, 0);
			if (resultCode == SOCKET_ERROR)
				return 0;
		}
	}

	::closesocket(listenSocket);
	::WSACleanup();
}

0개의 λŒ“κΈ€