전체 글 38

3. UDP 서버

TCP 서버를 알아보았으니 이제 코드를 수정해보면서 UDP 서버에 대해 알아보자. 일반적으로 TCP는 안정성이 높고 패킷의 도착순서를 보장하는 반면 UDP는 그렇지 못하지만 속도가 빠르고 바운더리(패킷과 패킷 사이의 경계)가 존재한다는 특징이 있다. 두 가지 전부 장단점이 있으므로 필요에 따라 적절한 프로토콜을 사용하면 된다. 채팅이나 아이템 거래 등과 같이 데이터가 유실되면 문제가 생기거나 패킷 수신의 순서가 중요한 경우 TCP를 사용하면 될 것이고, FPS와 같은 실시간 통신 게임이나 스트리밍 서비스와 같이 빠른 반응이 필요할 경우 UDP를 사용하면 될 것이다. DummyClient.cpp#include "pch.h"#include #include #include #include #pragma com..

서버/네트워크 2024.08.30

2. TCP 서버

간단한 서버의 구조를 만들어보았으니 이제 데이터를 보내고 받는 작업을 해보자. Dummyclient.cpp#include "pch.h"#include #include #include #include #pragma comment(lib, "ws2_32.lib")int main(){ // 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화) // 관련 정보가 wsaData에 채워짐 WSAData wsaData; if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 0; // af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6) // type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM) // ..

서버/네트워크 2024.08.29

1. 소켓 프로그래밍

드디어 뭔가 통신같아 보이는 파트로 넘어왔다. 바로 코드부터 살펴보자. DummyClient.cpp#include "pch.h"#include #include #include #include #pragma comment(lib, "ws2_32.lib")int main(){ // 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화) // 관련 정보가 wsaData에 채워짐 WSAData wsaData; if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return 0; // af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6) // type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM) // prot..

서버/네트워크 2024.08.29

8. Object Pool

저번의 메모리 풀에는 뭔가가 아쉬운 부분이 있다. 직접 구현한 메모리 풀에서는 사이즈가 비슷한 객체들을 하나의 풀에 넣어서 관리를 했다. 사이즈가 조금이라도 다른 객체들이 같은 풀에 있다보니 해당 풀에서 메모리 오염이 발생할 가능성이 있다. 만약 어떤 한 클래스에서 오류가 났다면 그 클래스의 코드만 살펴보면 되지만 이런 경우 어디서 오류가 발생했는지 탐지하기도 굉장히 힘들다. 그래서 이번에 알아볼 것이 오브젝트 풀이다. 생각해보면 알겠지만 메모리 풀이 오브젝트 풀의 상위 개념이다. 오브젝트 풀을 포함하고 있다는 것이다.  ObjectPool.h#pragma once#include "Types.h"#include "MemoryPool.h"templateclass ObjectPool{public: templ..

7. Memory Pool #2

ABA Problem이라는 것이 있다. CAS(Compare And Swap)에서 오류를 감지하지 못하는 것인데 쉽게 예시를 설명하자면 이렇다. 10 -> 20 -> 30 과 같이 리스트로 연결된 데이터가 있다고 하자.A 스레드가 CAS를 이용하여 제일 앞의 10을 40으로 바꾼다고 해보자. 그렇다면 10 -> 20 -> 30이라는 리스트를 저장 후 '만약 제일 앞의 데이터가 10이면 40으로 바꾸어라' 이렇게 작동할 것이다. 하지만 멀티스레드 환경에서 CAS를 실행하기 전 다른 여러 스레드에서 pop 2번이 일어나 10과 20의 메모리가 반환 되고, push 1번 일어나서 10이라는 메모리를 재활용해 다시 할당 받았다고 가정하여 10 -> 30이 되었다고 생각해보자. 그렇게 되면 제일 앞의 데이터는 1..

6. Memory Pool #1

지금까지는 필요할 때 메모리를 할당하고 사용이 끝나면 해제하였다. 하지만 매번 할당하고 해제하는 것이 비효율적이라고 느낄 수도 있고 메모리 단편화에 대한 문제도 있다.(요즘은 할당기가 잘 되어 있어 단편화에 대한 문제는 크게 신경쓸 필요는 없다고 한다.) 풀이라는 것은 직역하면 수영장 뭐 이런 뜻인데 수영장에 물을 담아놓는 것처럼 메모리를 담아놓았다가 필요할 때 꺼내 쓰는 것이다. 메모리를 사용하고 해제하는 것이 아니라 그냥 풀에 스윽 던져놓는 것이다. 그래서 매번 할당하고 해제할 필요성이 없다. 객체단위로 풀링을 하는 오브젝트 풀링이라는 것을 들어보았을 것이다. 하지만 우리가 동적할당할 것들은 객체뿐만 아니라 벡터, 맵 등 다양한 자료구조에 대해서도 동적할당을 해야한다. 이런 상황(크기가 일정하지 않음..

5. STL Allocator

일반적인 클래스나 자료형은 지금까지 했던 Allocator를 이용하여 메모리를 할당하고 해제할 수 있었다. 메모리 오염 또한 방지할 수 있었는데 벡터나 맵 등 다른 자료구조에 대해서는 아직 내부적으로 new, delete를 사용하고 있을 것이다. 이번에는 이를 한 번 수정해보자.벡터 클래스의 선언부를 보면 타입과 Allocator를 지정할 수 있다.vector> v;이런식으로 사용할 수 있다는 뜻이다!!! 하지만 매번 위와 같이 지정할 수는 없으므로 헤더파일을 하나 만들어보자. Container.h#pragma once#include "Types.h"#include "Allocator.h"#include #include #include #include #include #include #include #i..

4. Stomp Allocator

메모리가 이미 해제되었는데 사용하거나 사용할 수 있는 범위를 넘어서 사용하는 등 유효하지 않은 메모리 영역에 접근하는 것을 메모리 오염이라고 한다. 메모리 오염의 경우 크래시가 나지 않을 때도 있어 프로그램에 치명적일 수 있다. 예를 들어 허용된 범위를 넘어 메모리에 접근을 했는데 이미 다른 객체가 사용중인 메모리라던가 아니면 게임같은 경우 골드와 같이 유료재화를 담당하는 메모리라고 생각해보자. 이러한 상황을 예방하기 위한 것이 바로 Stomp Allocator이다. Allocator.h//-------------------// StompAllocator//-------------------class StompAllocator{ enum { PAGE_SIZE = 0x1000 }; // 4kbpublic..

3. Allocator

메모리를 관리하기 위해서 일반적으로 new와 delete를 사용한다. 하지만 메모리를 사용하고 반납하는 과정에서 조각난 공간들이 생기게 되고 그렇게 되면 분명 빈 메모리가 있음에도 제대로 사용할 수 없는 상황이 생기게 된다. 이런 상황을 메모리 단편화라고 한다. 이러한 상황을 예방할 수 있는 메모리풀을 구현하기 위한 초석으로 간단하게 new와 delete를 새롭게 만들어 보자. Allocator.h#pragma once//------------------// BaseAllocator//------------------class BaseAllocator{public: static void* Alloc(int32 size); static void Release(void* ptr);};Allocator.c..

2. 스마트포인터

스마트포인터는 동적 메모리를 관리하는 클래스이다. 일반적인 포인터와 똑같이 사용할 수 있지만 프로그래머가 직접 메모리 해제를 해주어야하는 포인터와 달리 스마트포인터는 알아서 메모리를 해제해준다. 스마트포인터는 3가지 종류가 있다. 1) unique_ptrunique_ptr ptr1 = make_unique(10);unique_ptr ptr2(new int(10));unique_ptr은 위와 같이 사용할 수 있고 unique라는 단어 뜻 그대로 다른 포인터가 해당 객체를 가르킬 수 없다. 그래서 복사도 불가능하다. 따라서 move와 같은 함수를 사용하여 객체의 소유권을 완전히 넘겨주는 식으로만 동작한다. 2) shared_ptrshared_ptr ptr1 = make_shared(10);shared_ptr..