<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>성댕쓰 똑똑한 생활</title>
    <link>https://dockdocklife.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 7 Apr 2026 23:14:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>성댕쓰</managingEditor>
    <item>
      <title>Unicode</title>
      <link>https://dockdocklife.tistory.com/entry/Unicode</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가변길이 데이터 중 문자열을 보내는 방법에 대해 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1683385848561&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;char sendData[1000] = &quot;가&quot;; // CP949
char sendData[1000] = u8&quot;가&quot;; // UTF-8 Unicode (한글 3바이트 + 로마 1바이트)
WCHAR sendData[1000] = L&quot;가&quot;; // UTF-16 (한글, 로마 2바이트)
TCHAR sendData[1000] = _T(&quot;가&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientPacketHandler.cpp&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683386540246&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct S_TEST
{
	uint64 id;
	uint32 hp;
	uint16 attack;
	// 가변 데이터
	// 1) 문자열 (ex. name)
	// 2) 그냥 바이트 배열 (ex. 길드 이미지)
	// 3) 일반 리스트
	vector&amp;lt;int64&amp;gt; buffs;

	wstring name;
};

...

void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);

	PacketHeader header;
	br &amp;gt;&amp;gt; header;

	...

	wstring name;
	uint16 nameLen;
	br &amp;gt;&amp;gt; nameLen;
	name.resize(nameLen);

	br.Read((void*)name.data(), nameLen * sizeof(WCHAR));

	wcout.imbue(std::locale(&quot;kor&quot;));
	wcout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; endl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ServerPacketHandler.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683386621419&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SendBufferRef ServerPacketHandler::Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector&amp;lt;BuffData&amp;gt; buffs, wstring name)
{
	SendBufferRef sendBuffer = GSendBufferManager-&amp;gt;Open(4096);
	
	BufferWriter bw(sendBuffer-&amp;gt;Buffer(), sendBuffer-&amp;gt;AllocSize());

	PacketHeader* header = bw.Reserve&amp;lt;PacketHeader&amp;gt;();
    
...

	bw &amp;lt;&amp;lt; (uint16)name.size();
	bw.Write((void*)name.data(), name.size() * sizeof(WCHAR));

	header-&amp;gt;size = bw.WriteSize();
	header-&amp;gt;id = S_TEST; // 1 : Test Msg

	sendBuffer-&amp;gt;Close(bw.WriteSize());

	return sendBuffer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 문자열 집합과 인코딩의 차이는?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 문자열 집합은 문자를 숫자에 매핑한 정보, 인코딩은 매핑한 정보를 어떻게 해석할 것인지에 대한 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. UTF-8 encoding 이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Unicode 문자열 집합을 해석하는 방법, e.g) 첫 바이트 마지막 비트가 0 이면 1바이트 만으로 해석하고 그렇지 않으면 2~3바이트로 해석하는 방법 (한글은 3바이트)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. UTF-16 encoding 이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Unicode 문자열 집합을 해석하는 방법. UTF-8 과 다르게 BMP 이외 모든 문자를 2바이트로 해석하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. CP949 란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Encoding 방법, 그런데 2 가지 문자집합을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. MBCS vs WBCS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MBCS 는 가변길이 인코딩, WBCS 는 고정길이 인코딩. MBCS 대표로 UTF-8, WBCS 는 UTF-16이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 : &lt;a href=&quot;https://www.inflearn.com/course/%EC%96%B8%EB%A6%AC%EC%96%BC-3d-mmorpg-4/dashboard&quot;&gt;[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 대시보드 - 인프런 | 강의 (inflearn.com)&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++  게임개발</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/226</guid>
      <comments>https://dockdocklife.tistory.com/entry/Unicode#entry226comment</comments>
      <pubDate>Sun, 7 May 2023 00:26:19 +0900</pubDate>
    </item>
    <item>
      <title>PacketHandler</title>
      <link>https://dockdocklife.tistory.com/entry/PacketHandler</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 시간에 만든 Packet을 쓰거나 읽을 때 필요한 공통적인 부분을 모듈화 해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ServerPacketHandler.h, ServerPacketHandler.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683300116078&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
enum
{
	S_TEST = 1 
}; 

struct BuffData
{
	uint64 buffId;
	float remainTime;
};

class ServerPacketHandler
{
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static SendBufferRef Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector&amp;lt;BuffData&amp;gt; buffs);
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683300138938&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;ServerPacketHandler.h&quot;
#include &quot;BufferReader.h&quot;
#include &quot;BufferWriter.h&quot;

void ServerPacketHandler::HandlePacket(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);
	
	PacketHeader header;
	br.Peek(&amp;amp;header);

	switch (header.id)
	{
	default:
		break;
	}
}

SendBufferRef ServerPacketHandler::Make_S_TEST(uint64 id, uint32 hp, uint16 attack, vector&amp;lt;BuffData&amp;gt; buffs)
{
	SendBufferRef sendBuffer = GSendBufferManager-&amp;gt;Open(4096);
	
	BufferWriter bw(sendBuffer-&amp;gt;Buffer(), sendBuffer-&amp;gt;AllocSize());

	PacketHeader* header = bw.Reserve&amp;lt;PacketHeader&amp;gt;();
	// id(uint64), 체력(uint32), 공격력(uint16)
	bw &amp;lt;&amp;lt; id &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; attack;

	// 가변 데이터
	bw &amp;lt;&amp;lt; (uint16)buffs.size();
	for (BuffData&amp;amp; buff : buffs)
	{
		bw &amp;lt;&amp;lt; buff.buffId &amp;lt;&amp;lt; buff.remainTime;
	}

	header-&amp;gt;size = bw.WriteSize();
	header-&amp;gt;id = S_TEST; // 1 : Test Msg

	sendBuffer-&amp;gt;Close(bw.WriteSize());

	return sendBuffer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientPacketHandler.h, ClientPacketHandler.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683300367900&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

enum
{
	S_TEST = 1
};

class ClientPacketHandler
{
public:
	static void HandlePacket(BYTE* buffer, int32 len);

	static void Handle_S_TEST(BYTE* buffer, int32 len);
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683300387830&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;ClientPacketHandler.h&quot;
#include &quot;BufferReader.h&quot;

void ClientPacketHandler::HandlePacket(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);

	PacketHeader header;
	br &amp;gt;&amp;gt; header;

	switch (header.id)
	{
	case S_TEST:
		Handle_S_TEST(buffer, len);
		break;
	}
}

// 패킷 설계 TEMP
struct BuffData
{
	uint64 buffId;
	float remainTime;
};

struct S_TEST
{
	uint64 id;
	uint32 hp;
	uint16 attack;
	// 가변 데이터
	// 1) 문자열 (ex. name)
	// 2) 그냥 바이트 배열 (ex. 길드 이미지)
	// 3) 일반 리스트
	vector&amp;lt;int64&amp;gt; buffs;
};

void ClientPacketHandler::Handle_S_TEST(BYTE* buffer, int32 len)
{
	BufferReader br(buffer, len);

	PacketHeader header;
	br &amp;gt;&amp;gt; header;

	uint64 id;
	uint32 hp;
	uint16 attack;
	br &amp;gt;&amp;gt; id &amp;gt;&amp;gt; hp &amp;gt;&amp;gt; attack;

	cout &amp;lt;&amp;lt; &quot;ID: &quot; &amp;lt;&amp;lt; id &amp;lt;&amp;lt; &quot; HP : &quot; &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; &quot; ATT : &quot; &amp;lt;&amp;lt; attack &amp;lt;&amp;lt; endl;

	vector&amp;lt;BuffData&amp;gt; buffs;
	uint16 buffCount;
	br &amp;gt;&amp;gt; buffCount;

	buffs.resize(buffCount);
	for (int32 i = 0; i &amp;lt; buffCount; i++)
	{
		br &amp;gt;&amp;gt; buffs[i].buffId &amp;gt;&amp;gt; buffs[i].remainTime;
	}

	cout &amp;lt;&amp;lt; &quot;BuffCount : &quot; &amp;lt;&amp;lt; buffCount &amp;lt;&amp;lt; endl;
	for (int32 i = 0; i &amp;lt; buffCount; i++)
	{
		cout &amp;lt;&amp;lt; &quot;BufInfo : &quot; &amp;lt;&amp;lt; buffs[i].buffId &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; buffs[i].remainTime &amp;lt;&amp;lt; endl;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameSession.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683300192072&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void GameSession::OnRecvPacket(BYTE* buffer, int32 len)
{
	ServerPacketHandler::HandlePacket(buffer, len);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameServer.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683300403303&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

while (true)
{
    vector&amp;lt;BuffData&amp;gt; buffs{ BuffData{100, 1.5f}, BuffData{200, 2.3f}, BuffData{300, 0.7f} };
    SendBufferRef sendBuffer = ServerPacketHandler::Make_S_TEST(1001, 100, 10, buffs);

    GSessionManager.Broadcast(sendBuffer);

    this_thread::sleep_for(250ms);
}

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DummyClient.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1683300318912&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

virtual void OnRecvPacket(BYTE* buffer, int32 len) override
{
    ClientPacketHandler::HandlePacket(buffer, len);
}

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BufferWriter.h&lt;/p&gt;
&lt;pre id=&quot;code_1683300064599&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BufferWriter
{
	...
    
    // 삭제 start
    template&amp;lt;typename T&amp;gt;
	BufferWriter&amp;amp; operator&amp;lt;&amp;lt;(const T&amp;amp; src);
    // 삭제 end
}

...

 // 삭제 start
template&amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(const T&amp;amp; src)
{
	*reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]) = src;
	_pos += sizeof(T);
	return *this;
}
 // 삭제 end

template&amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(T&amp;amp;&amp;amp; src)
{
	using DataType = std::remove_reference_t&amp;lt;T&amp;gt;;
	*reinterpret_cast&amp;lt;DataType*&amp;gt;(&amp;amp;_buffer[_pos]) = std::forward&amp;lt;DataType&amp;gt;(src);
	_pos += sizeof(T);
	return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 기존 방법 안좋은 점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Write 한 순서대로 Read 해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Packet 지금 1 종류임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 보편참조가 일어나는 조건은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rvalue 파라메터로 받는 template 함수, 같은 파라메터 이지만 lvalue로 받는 template 함수 있을 때, 전자 template 함수만 쓰이게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보편참조 함수에 lvalue 들어오면 &quot;const T&amp;amp;&quot; 로 rvalue 이면 &quot;T&amp;amp;&amp;amp;&quot;로 들어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. BufferWriter operator&amp;lt;&amp;lt;에 컴파일 에러가 생기는 이유와 해결방?&lt;br /&gt;- 보편참조 함수에 uint64 들어옴 &amp;gt; 함수 안에서 const uint64&amp;amp; (= T값)의 포인터를 추출하려고 시도하기 때문임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 레퍼런스를 뺀 나머지만 이용하여 연산하면 해결됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- std::remove_reference_t&amp;lt;T&amp;gt; 는 T에 레퍼런스가 있으면 떼줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 : &lt;a href=&quot;https://www.inflearn.com/course/%EC%96%B8%EB%A6%AC%EC%96%BC-3d-mmorpg-4/dashboard&quot;&gt;[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 대시보드 - 인프런 | 강의 (inflearn.com)&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++  게임개발</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/225</guid>
      <comments>https://dockdocklife.tistory.com/entry/PacketHandler#entry225comment</comments>
      <pubDate>Sat, 6 May 2023 00:27:28 +0900</pubDate>
    </item>
    <item>
      <title>Buffer Helpers</title>
      <link>https://dockdocklife.tistory.com/entry/Buffer-Helpers</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼에 데이터 쓰고 읽는데 도움주는 클래스를 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BufferReader.h, BufferReader.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1682205374548&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

/*--------------------
	BufferReader
--------------------*/

class BufferReader
{
public:
	BufferReader();
	BufferReader(BYTE* buffer, uint32 size, uint32 pos = 0);
	~BufferReader();

	BYTE* Buffer() { return _buffer; }
	uint32 Size() { return _size; }
	uint32 ReadSize() { return _pos; }
	uint32 FreeSize() { return _size - _pos; }

	template&amp;lt;typename T&amp;gt;
	bool Peek(T* dest) { return Peek(dest, sizeof(T)); }
	bool Peek(void* dest, uint32 len);

	template&amp;lt;typename T&amp;gt;
	bool Read(T* dest) { return Read(dest, sizeof(T)); }
	bool Read(void* dest, uint32 len);

	template&amp;lt;typename T&amp;gt;
	BufferReader&amp;amp; operator&amp;gt;&amp;gt;(OUT T&amp;amp; dest);

private:
	BYTE* _buffer = nullptr;
	uint32 _size = 0;
	uint32 _pos = 0;

};

template&amp;lt;typename T&amp;gt;
BufferReader&amp;amp; BufferReader::operator&amp;gt;&amp;gt;(OUT T&amp;amp; dest)
{
	dest = *reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]);
	_pos += sizeof(T);
	return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1682205353448&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;BufferReader.h&quot;

/*--------------------
	BufferReader
--------------------*/

BufferReader::BufferReader()
{
}

BufferReader::BufferReader(BYTE* buffer, uint32 size, uint32 pos /*=0*/)
	:_buffer(buffer), _size(size), _pos(pos)
{
}

BufferReader::~BufferReader()
{
}

bool BufferReader::Peek(void* dest, uint32 len)
{
	if (FreeSize() &amp;lt; len)
		return false;

	::memcpy(dest, &amp;amp;_buffer[_pos], len);
	return true;
}

bool BufferReader::Read(void* dest, uint32 len)
{
	if (Peek(dest, len) == false)
		return false;

	_pos += len;
	return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BufferWriter.h, BufferWriter.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1682205423724&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
/*--------------------
	BufferWriter
--------------------*/

class BufferWriter
{
public:
	BufferWriter();
	BufferWriter(BYTE* buffer, uint32 size, uint32 pos = 0);
	~BufferWriter();

	BYTE* Buffer() { return _buffer; }
	uint32 Size() { return _size; }
	uint32 WriteSize() { return _pos; }
	uint32 FreeSize() { return _size - _pos; }

	template&amp;lt;typename T&amp;gt;
	bool Write(T* src) { return Write(src, sizeof(T)); }
	bool Write(void* src, uint32 len);

	template&amp;lt;typename T&amp;gt;
	T* Reserve();

	template&amp;lt;typename T&amp;gt;
	BufferWriter&amp;amp; operator&amp;lt;&amp;lt;(const T&amp;amp; src);

	template&amp;lt;typename T&amp;gt;
	BufferWriter&amp;amp; operator&amp;lt;&amp;lt;(T&amp;amp;&amp;amp; src);

private:
	BYTE* _buffer = nullptr;
	uint32 _size = 0;
	uint32 _pos = 0;

};

template&amp;lt;typename T&amp;gt;
T* BufferWriter::Reserve()
{
	if (FreeSize() &amp;lt; sizeof(T))
		return nullptr;

	T* ret = reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]);
	_pos += sizeof(T);
	return ret;
}

template&amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(const T&amp;amp; src)
{
	*reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]) = src;
	_pos += sizeof(T);
	return *this;
}

template&amp;lt;typename T&amp;gt;
BufferWriter&amp;amp; BufferWriter::operator&amp;lt;&amp;lt;(T&amp;amp;&amp;amp; src)
{
	*reinterpret_cast&amp;lt;T*&amp;gt;(&amp;amp;_buffer[_pos]) = std::move(src);
	_pos += sizeof(T);
	return *this;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1682205441540&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;BufferWriter.h&quot;

/*--------------------
	BufferWriter
--------------------*/

BufferWriter::BufferWriter()
{
}

BufferWriter::BufferWriter(BYTE* buffer, uint32 size, uint32 pos)
	:_buffer(buffer), _size(size), _pos(pos)
{
}

BufferWriter::~BufferWriter()
{
}

bool BufferWriter::Write(void* src, uint32 len)
{
	if (FreeSize() &amp;lt; len)
		return false;

	::memcpy(&amp;amp;_buffer[_pos], src, len);
	_pos += len;
	return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BufferWriter는 아래처럼 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameServer.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1682205593462&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
while (true)
{
    SendBufferRef sendBuffer = GSendBufferManager-&amp;gt;Open(4096);
    
    BufferWriter bw(sendBuffer-&amp;gt;Buffer(), 4096);

    PacketHeader* header = bw.Reserve&amp;lt;PacketHeader&amp;gt;();
    // id(uint64), 체력(uint32), 공격력(uint16)
    bw &amp;lt;&amp;lt; (uint64)1001 &amp;lt;&amp;lt; (uint32)100 &amp;lt;&amp;lt; (uint16)10;
    bw.Write(sendData, sizeof(sendData));

    header-&amp;gt;size = bw.WriteSize();
    header-&amp;gt;id = 1; // 1 : Test Msg

    sendBuffer-&amp;gt;Close(bw.WriteSize());
    
    GSessionManager.Broadcast(sendBuffer);

    this_thread::sleep_for(250ms);
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BufferReader는 아래처럼 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DummyClient.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1682205699134&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
virtual int32 OnRecvPacket(BYTE* buffer, int32 len) override
{
    BufferReader br(buffer, len);

    PacketHeader header;
    br &amp;gt;&amp;gt; header;

    uint64 id;
    uint32 hp;
    uint16 attack;
    br &amp;gt;&amp;gt; id &amp;gt;&amp;gt; hp &amp;gt;&amp;gt; attack;

    cout &amp;lt;&amp;lt; &quot;ID: &quot; &amp;lt;&amp;lt; id &amp;lt;&amp;lt; &quot; HP : &quot; &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; &quot; ATT : &quot; &amp;lt;&amp;lt; attack &amp;lt;&amp;lt; endl;

    char recvBuffer[4096];
    br.Read(recvBuffer, header.size - sizeof(PacketHeader) - 8 - 4 - 2);
    cout &amp;lt;&amp;lt; recvBuffer &amp;lt;&amp;lt; endl;

    return len;
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. BufferReader operator &amp;gt;&amp;gt; 이 하는 역할은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터를 읽고 position을 읽은 만큼 이동시킴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Peek 의 역할은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터를 파싱할 수 있는지 확인함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. BufferReader 사용법과 설명, BufferWriter 사용법과 설명?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- BufferReader : Iocp Recv 이벤트 받음&amp;gt; ProcessRecv &amp;gt;&amp;nbsp; OnRecv &amp;gt; OnRecvPacket 에서 write한 순서대로 읽음 마지막 len 계산은 가변길이 정보 읽는 방법 보여준 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- BufferWriter : SendBufferRef 생성 &amp;gt; BufferWriter 생성 &amp;gt; '&amp;lt;&amp;lt;' operator로 write &amp;gt; 'Hello World' 가변길이 데이터 보내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++  게임개발</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/224</guid>
      <comments>https://dockdocklife.tistory.com/entry/Buffer-Helpers#entry224comment</comments>
      <pubDate>Wed, 19 Apr 2023 22:38:50 +0900</pubDate>
    </item>
    <item>
      <title>Packet Session</title>
      <link>https://dockdocklife.tistory.com/entry/Packet-Session</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Session.h, Session.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681790113584&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
/*----------------
  PacketSession
----------------*/

struct PacketHeader
{
	uint16 size;
	uint16 id; // 프로토콜ID (ex. 1=로그인, 2=이동요청)
};

// [size(2)][id(2)][data...]...[size(2)][id(2)][data...]
class PacketSession : public Session
{
public:
	PacketSession();
	virtual ~PacketSession();

	PacketSessionRef GetPacketSessionRef() { return static_pointer_cast&amp;lt;PacketSession&amp;gt;(shared_from_this()); }

protected:
	virtual int32 OnRecv(BYTE* buffer, int32 len) sealed;
	virtual int32 OnRecvPacket(BYTE* buffer, int32 len) abstract;
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681790505107&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
void Session::Send(SendBufferRef sendBuffer)
{
    if (IsConnected() == false)
        return;

    bool registerSend = false;
    
    // 현재 RegisterSend가 걸리지 않은 상태라면, 걸어준다
    {
		WRITE_LOCK;

		_sendQueue.push(sendBuffer);

        if (_sendRegistered.exchange(true) == false)
            registerSend = true;
    }

    if (registerSend)
        RegisterSend();
}
...
void Session::Disconnect(const WCHAR* cause)
{
    if (_connected.exchange(false) == false)
        return;

    // TEMP
    wcout &amp;lt;&amp;lt; &quot;Disconnect : &quot; &amp;lt;&amp;lt; cause &amp;lt;&amp;lt; endl;

    RegisterDisconnect();
}
...
void Session::ProcessDisconnect()
{
    _disconnectEvent.owner = nullptr; // RELEASE_REF
    
    OnDisconnected(); // 컨텐츠 코드에서 오버라이딩
    GetService()-&amp;gt;ReleaseSession(GetSessionRef());
}
...
/*----------------
  PacketSession
----------------*/

PacketSession::PacketSession()
{
}

PacketSession::~PacketSession()
{
}

// [size(2)][id(2)][data...]...[size(2)][id(2)][data...]
int32 PacketSession::OnRecv(BYTE* buffer, int32 len)
{
    int32 processLen = 0;

    while (true)
    {
        int32 dataSize = len - processLen;
        // 최소한 헤더는 파싱할 수 있어야 한다.
        if (dataSize &amp;lt; sizeof(PacketHeader))
            break;

        PacketHeader header = *(reinterpret_cast&amp;lt;PacketHeader*&amp;gt;(&amp;amp;buffer[processLen]));
        // 헤더에 기록된 패킷 크기를 파싱할 수 있어야 한다.
        if (dataSize &amp;lt; header.size)
            break;

        // 패킷 조립 성공
        OnRecvPacket(&amp;amp;buffer[processLen], header.size);

        processLen += header.size;
    }

    return processLen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameSession.h, GameSession.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681790563683&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
class GameSession : public PacketSession
{
public:
	~GameSession()
	{
		cout &amp;lt;&amp;lt; &quot;~GameSession&quot; &amp;lt;&amp;lt; endl;
	}

	virtual void OnConnected() override;
	virtual void OnDisconnected() override;
	virtual int32 OnRecvPacket(BYTE* buffer, int32 len) override;

	virtual void OnSend(int32 len) override;
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681790619020&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
int32 GameSession::OnRecvPacket(BYTE* buffer, int32 len)
{
	PacketHeader header = *((PacketHeader*)&amp;amp;buffer[0]);
	cout &amp;lt;&amp;lt; &quot;Packet ID : &quot; &amp;lt;&amp;lt; header.id &amp;lt;&amp;lt; &quot;Size : &quot; &amp;lt;&amp;lt; header.size &amp;lt;&amp;lt; endl;

	return len;
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GameServer.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681790752763&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
#include &quot;GameSessionManager.h&quot;

int main()
{
...
	char sendData[1000] = &quot;Hello World&quot;;

	while (true)
	{
		SendBufferRef sendBuffer = GSendBufferManager-&amp;gt;Open(4096);
		
		BYTE* buffer = sendBuffer-&amp;gt;Buffer();
		((PacketHeader*)buffer)-&amp;gt;size = (sizeof(sendData) + sizeof(PacketHeader));
		((PacketHeader*)buffer)-&amp;gt;id = 1; // 1 : Hello Msg
		::memcpy(&amp;amp;buffer[4], sendData, sizeof(sendData));
		sendBuffer-&amp;gt;Close((sizeof(sendData) + sizeof(PacketHeader)));
		
		GSessionManager.Broadcast(sendBuffer);

		this_thread::sleep_for(250ms);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DummyClient.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681790845314&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
class ServerSession : public PacketSession
{
...
	virtual void OnConnected() override
	{
		//cout &amp;lt;&amp;lt; &quot;Connected To Server&quot; &amp;lt;&amp;lt; endl;
	}

	virtual int32 OnRecvPacket(BYTE* buffer, int32 len) override
	{
		PacketHeader header = *((PacketHeader*)&amp;amp;buffer[0]);
		//cout &amp;lt;&amp;lt; &quot;Packet ID : &quot; &amp;lt;&amp;lt; header.id &amp;lt;&amp;lt; &quot;Size : &quot; &amp;lt;&amp;lt; header.size &amp;lt;&amp;lt; endl;

		char recvBuffer[4096];
		::memcpy(recvBuffer, &amp;amp;buffer[4], header.size - sizeof(PacketHeader));
		cout &amp;lt;&amp;lt; recvBuffer &amp;lt;&amp;lt; endl;

		return len;
	}

	virtual void OnSend(int32 len) override
	{
		//cout &amp;lt;&amp;lt; &quot;OnSend Len = &quot; &amp;lt;&amp;lt; len &amp;lt;&amp;lt; endl;
	}

	virtual void OnDisconnected() override
	{
		//cout &amp;lt;&amp;lt; &quot;Disconnected&quot; &amp;lt;&amp;lt; endl;
	}
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Session::Send 효율적이지 못한 부분은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어느 한 쓰레드가 RegisterSend 호출 중 이라면 WriteLock에서 다른 많은 Send 요청이 대기 할 수 있음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. PacketSession 이 필요한 이유는?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보낸 모든 데이터를 한 번에 받는 다는 보장이 없는 TCP 특성 때문에, 데이터 모두 받았음을 확인 할 수 있는 수단이 필요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. PacketSession::OnRecv에서 header가 덜 들어왔거나 header만 들어온 상황은 어떻게 처리하지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- RegisterRecv() &amp;gt; ProcessRecv() 호출 되면서 아직 들어오지 않은 부분 들어옴.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 접속 Client의 개수를 1000개로 늘리면, server broadcast에서 crash 나는 이유는 그리고 해결방법?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sessions iterate 하다가 sessions 에 변화가 생겨서 남. OnDisconnect에서 session remove하고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해결방법 : Disconnect에서 session remove하지 않고 ProcessDisconnect에서 한다.&lt;/p&gt;</description>
      <category>똑똑한 개발/C++  게임개발</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/223</guid>
      <comments>https://dockdocklife.tistory.com/entry/Packet-Session#entry223comment</comments>
      <pubDate>Sun, 16 Apr 2023 21:25:25 +0900</pubDate>
    </item>
    <item>
      <title>SendBuffer Pooling</title>
      <link>https://dockdocklife.tistory.com/entry/SendBuffer-Pooling</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Sendbuffer를 Pooling 하여 사용하도록 바꿔보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CoreTLS.h, CoreTLS.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681479758527&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
#include &amp;lt;stack&amp;gt;

extern thread_local uint32 LThreadId;
extern thread_local std::stack&amp;lt;int32&amp;gt; LLockStack;
extern thread_local SendBufferChunkRef LSendBufferChunk;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681479782521&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;CoreTLS.h&quot;

thread_local uint32 LThreadId = 0;
thread_local std::stack&amp;lt;int32&amp;gt; LLockStack;
thread_local SendBufferChunkRef LSendBufferChunk;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SendBuffer.h, SendBuffer.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1681479841485&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

class SendBufferChunk;
/*-----------
  SendBuffer
-----------*/

class SendBuffer
{
public:
	SendBuffer(SendBufferChunkRef owner, BYTE* buffer, int32 allocSize);
	~SendBuffer();

	BYTE* Buffer() { return _buffer; }
	int32 WriteSize() { return _writeSize; }
	void Close(uint32 writeSize);

private:
	BYTE* _buffer;
	uint32 _allocSize = 0;
	uint32 _writeSize = 0;
	SendBufferChunkRef _owner;
};

/*---------------------
  SendBufferChunk
---------------------*/

class SendBufferChunk : public enable_shared_from_this&amp;lt;SendBufferChunk&amp;gt;
{
	enum
	{
		SEND_BUFFER_CHUNK_SIZE = 6000
	};

public:
	SendBufferChunk();
	~SendBufferChunk();

	void Reset();
	SendBufferRef Open(uint32 allocSize);
	void Close(uint32 writeSize);

	bool IsOpen() { return _open; }
	BYTE* Buffer() { return &amp;amp;_buffer[_usedSize]; }
	uint32 FreeSize() { return static_cast&amp;lt;uint32&amp;gt;(_buffer.size()) - _usedSize; }

private:
	Array&amp;lt;BYTE, SEND_BUFFER_CHUNK_SIZE&amp;gt; _buffer = {};
	bool _open = false;
	uint32 _usedSize = 0;
};

/*---------------------
  SendBufferManager
---------------------*/

class SendBufferManager
{
public:
	SendBufferRef Open(uint32 size);

private:
	SendBufferChunkRef Pop();
	void Push(SendBufferChunkRef buffer);

	static void PushGlobal(SendBufferChunk* buffer);

private:
	USE_LOCK;
	Vector&amp;lt;SendBufferChunkRef&amp;gt; _sendBufferChunks;
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681479910237&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;SendBuffer.h&quot;

/*-----------
  SendBuffer
-----------*/

SendBuffer::SendBuffer(SendBufferChunkRef owner, BYTE* buffer, int32 allocSize)
	: _owner(owner), _buffer(buffer), _allocSize(allocSize)
{

}

SendBuffer::~SendBuffer()
{
}

void SendBuffer::Close(uint32 writeSize)
{
	ASSERT_CRASH(_allocSize &amp;gt;= writeSize);
	_writeSize = writeSize;
	_owner-&amp;gt;Close(writeSize);
}

/*---------------------
  SendBufferChunk
---------------------*/

SendBufferChunk::SendBufferChunk()
{
}

SendBufferChunk::~SendBufferChunk()
{
}

void SendBufferChunk::Reset()
{
	_open = false;
	_usedSize = 0;
}

SendBufferRef SendBufferChunk::Open(uint32 allocSize)
{
	ASSERT_CRASH(allocSize &amp;lt;= SEND_BUFFER_CHUNK_SIZE);
	ASSERT_CRASH(_open == false);

	if (allocSize &amp;gt; FreeSize())
		return nullptr;

	_open = true;
	return ObjectPool&amp;lt;SendBuffer&amp;gt;::MakeShared(shared_from_this(), Buffer(), allocSize);
}

void SendBufferChunk::Close(uint32 writeSize)
{
	ASSERT_CRASH(_open == true);
	_open = false;
	// cursor 옮기기
	_usedSize += writeSize;
}

/*---------------------
  SendBufferManager
---------------------*/

//[                 ] 많이 예약해두고 일부만 사용
SendBufferRef SendBufferManager::Open(uint32 size)
{
	if (LSendBufferChunk == nullptr)
	{ 
		LSendBufferChunk = Pop(); // WRITE_LOCK
		LSendBufferChunk-&amp;gt;Reset();
	} 

	ASSERT_CRASH(LSendBufferChunk-&amp;gt;IsOpen() == false);
	
	// 다 썼으면 버리고 새것으로 교체
	if (LSendBufferChunk-&amp;gt;FreeSize() &amp;lt; size)
	{
		LSendBufferChunk = Pop(); // WRITE_LOCK
		LSendBufferChunk-&amp;gt;Reset();
	}

	cout &amp;lt;&amp;lt; &quot;Free : &quot; &amp;lt;&amp;lt; LSendBufferChunk-&amp;gt;FreeSize() &amp;lt;&amp;lt; endl;

	return LSendBufferChunk-&amp;gt;Open(size);
}

SendBufferChunkRef SendBufferManager::Pop()
{
	{
		WRITE_LOCK;
		if (_sendBufferChunks.empty() == false)
		{
			SendBufferChunkRef sendBufferChunk = _sendBufferChunks.back();
			_sendBufferChunks.pop_back();
			return sendBufferChunk;
		}
	}

	return SendBufferChunkRef(xnew&amp;lt;SendBufferChunk&amp;gt;(), PushGlobal);
}

void SendBufferManager::Push(SendBufferChunkRef buffer)
{
	WRITE_LOCK;
	_sendBufferChunks.push_back(buffer);
}

void SendBufferManager::PushGlobal(SendBufferChunk* buffer)
{
	GSendBufferManager-&amp;gt;Push(SendBufferChunkRef(buffer, PushGlobal));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선할 점? 매번마다 OnRecv에서 MakeShared ?? 근데, 메모리 풀 사용하잖아? 그럼에도 메모리 양이 커지면 pooling 안하고 new, delete 방식임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 미리 많이 잡아서 sendbuffer 잡는 게 낭비&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 할당 (SendBufferChunk ) 잘라먹기 가 포인트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SendBufferManager에서 더 이상 사용하지 않는 메모리 관리하는 방법은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 메모리 풀에 해당 메모리 반납&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SendBufferChunkRef를 tls에 만드는 이유는?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- lock 사용을 최소화 하기 위함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당 한 Chunk 메모리를 모두 사용하여 새 거로 교체되면 이전 메모리는 해제 되지 않음을 어떻게 보장하는지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SendBuffer는 WSASend에서 Ref 카운팅 되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 : &lt;a href=&quot;https://www.inflearn.com/course/%EC%96%B8%EB%A6%AC%EC%96%BC-3d-mmorpg-4/dashboard&quot;&gt;[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 대시보드 - 인프런 | 강의 (inflearn.com)&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++  게임개발</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/222</guid>
      <comments>https://dockdocklife.tistory.com/entry/SendBuffer-Pooling#entry222comment</comments>
      <pubDate>Wed, 5 Apr 2023 22:54:44 +0900</pubDate>
    </item>
    <item>
      <title>DllMain 정리</title>
      <link>https://dockdocklife.tistory.com/entry/DllMain-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h1 id=&quot;dllmain&quot; data-line=&quot;0&quot;&gt;DllMain&lt;/h1&gt;
&lt;p data-line=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;Dynamic link library로 링크할 경우, DllMain이 진입점 함수로 불린다.&lt;/p&gt;
&lt;p data-line=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;기본 함수 모양은 다음과 같다.&lt;br /&gt;함수 이름은 대소문자 구별하므로 주의하자.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-line=&quot;45&quot; data-ke-size=&quot;size16&quot;&gt;DllMain은 프로세스 시작이나 쓰레드 시작할 때 호출된다.&lt;br /&gt;fdwReason에 각 상황에 따라 다음 4가지 중 하나의 값으로 들어온다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;47&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;47&quot;&gt;DLL_ATTACH_PROCESS&lt;/li&gt;
&lt;li data-line=&quot;48&quot;&gt;DLL_DETACH_PROCESS&lt;/li&gt;
&lt;li data-line=&quot;49&quot;&gt;DLL_ATTACH_THREAD&lt;/li&gt;
&lt;li data-line=&quot;50&quot;&gt;DLL_DETACH_THREAD&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;52&quot; data-ke-size=&quot;size16&quot;&gt;lpReserved는 DLL_PROCESS_ATTACH인 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;53&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;53&quot;&gt;Dll이 명시적 런타임 링크 방식으로 로드 되면 NULL&lt;/li&gt;
&lt;li data-line=&quot;54&quot;&gt;암시적 로드타임 링크 방식이면 not NULL&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;56&quot; data-ke-size=&quot;size16&quot;&gt;값을 갖는다.&lt;/p&gt;
&lt;p data-line=&quot;58&quot; data-ke-size=&quot;size16&quot;&gt;DLL_PROCESS_DETACH인 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;59&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;59&quot;&gt;FreeLibrary 호출 또는 Dll 로드 실패이면 NULL&lt;/li&gt;
&lt;li data-line=&quot;60&quot;&gt;프로세스가 종료중이라면 not NULL&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;62&quot; data-ke-size=&quot;size16&quot;&gt;값을 갖는다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h1 id=&quot;%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD&quot; data-line=&quot;61&quot;&gt;주의사항&lt;/h1&gt;
&lt;p data-line=&quot;66&quot; data-ke-size=&quot;size16&quot;&gt;DllMain이 호출되었을 때 다른 Dll은 로드되지 않았거나, DllMain이 호출되지 않았을 수도 있다.&lt;br /&gt;따라서, DllMain에서 다른 dll의 함수를 호출하면 안된다.&lt;br /&gt;또한 Dll 의존 루프가 발생할 수도 있어서, LoadLibrary, FreeLibrary도 호출해선 안된다.&lt;/p&gt;
&lt;p data-line=&quot;70&quot; data-ke-size=&quot;size16&quot;&gt;DllMain 함수를 만들지 않으면 C/C++ 런타임 라이브러리 DllMain 함수가 사용된다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h1 id=&quot;dllmain%EC%9D%98-%EB%84%A4-%EA%B0%80%EC%A7%80-%ED%86%B5%EC%A7%80&quot; data-line=&quot;69&quot;&gt;DllMain의 네 가지 통지&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-line=&quot;74&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;74&quot;&gt;DLL_PROCESS_ATTACH&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;763&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUoGvn/btrKuydJpjp/JmYAEhDRDcJw2r338Q1t50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUoGvn/btrKuydJpjp/JmYAEhDRDcJw2r338Q1t50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUoGvn/btrKuydJpjp/JmYAEhDRDcJw2r338Q1t50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUoGvn%2FbtrKuydJpjp%2FJmYAEhDRDcJw2r338Q1t50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;763&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;763&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. DLL_PROCESS_DETACH&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-line=&quot;78&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;78&quot;&gt;프로세스 종료
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;79&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;79&quot;&gt;특정 쓰레드가 TerminateProcess 함수 호출하여 프로세스 종료하면,&lt;br /&gt;다른 DllMain 호출하지 않음.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2. FreeLibrary 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3. DLL_THREAD_ATTACH&lt;br /&gt;프로세스 내에 새로운 쓰레드가 생성되면,&lt;br /&gt;시스템은 프로세스에 로드된 모든 dll의 DllMain함수를&lt;br /&gt;DLL_THRAD_ATTACH값을 전달하여 호출한다.&lt;br /&gt;하지만 쓰레드가 먼저 생성되고 Dll이 로드 되었다면 호출되지 않는다.&lt;br /&gt;또, DLL_PROCESS_ATTACH를 호출한 쓰레드에 대해선 호출하지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. DLL_THREAD_DETACH&lt;br /&gt;시스템은 ExitThread를 호출할 때 스레드를 바로 죽이지 않고&lt;br /&gt;프로세스 주소 공간에 매핑된 모든 dll의 DLL_THREAD_DETACH 값을 인자로 하여 호출하고&lt;br /&gt;모든 Dll이 DllMain함수를 반환하면 그 때 쓰레드를 종료한다.&lt;br /&gt;만약, 특정 스레드가 TerminateThread 함수를 호출하여 스레드를 종료한 경우&lt;br /&gt;DLL_THREAD_DETACH 값으로 어떤 DllMain 함수도 호출되지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-line=&quot;97&quot; data-ke-size=&quot;size16&quot;&gt;참고 :&lt;br /&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain?redirectedfrom=MSDN&quot;&gt;https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain?redirectedfrom=MSDN&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://egloos.zum.com/sweeper/v/2991972&quot;&gt;http://egloos.zum.com/sweeper/v/2991972&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/186</guid>
      <comments>https://dockdocklife.tistory.com/entry/DllMain-%EC%A0%95%EB%A6%AC#entry186comment</comments>
      <pubDate>Wed, 24 Aug 2022 15:13:11 +0900</pubDate>
    </item>
    <item>
      <title>Strand 정리</title>
      <link>https://dockdocklife.tistory.com/entry/Strand-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h1 id=&quot;strands-%EB%9E%80&quot; data-line=&quot;0&quot;&gt;Strands 란&lt;/h1&gt;
&lt;p data-line=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;event handler 순차 실행을 보장하는 api.&lt;br /&gt;이를 활용하면, 멀티 쓰레드 환경에서 명시적 lock없이 코드를 만들 수 있다.&lt;br /&gt;&lt;br /&gt;strand는 application code와 handler 실행 사이에 layer를 제공한다.&lt;br /&gt;worker thread가 직접 handler를 호출하지 않고 strand queue에 쌓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LMTo9/btrKj0isajZ/8Xf7C2vhtkBhpoJ4J9fMT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LMTo9/btrKj0isajZ/8Xf7C2vhtkBhpoJ4J9fMT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LMTo9/btrKj0isajZ/8Xf7C2vhtkBhpoJ4J9fMT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLMTo9%2FbtrKj0isajZ%2F8Xf7C2vhtkBhpoJ4J9fMT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;349&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;strand-implementation&quot; data-line=&quot;11&quot;&gt;Strand Implementation&lt;/h1&gt;
&lt;p data-line=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;Strand가 보장해야 할 기능은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;13&quot;&gt;동시에 핸들러를 실행하지 않는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;14&quot;&gt;이를 만족하기 위해, worker 스레드가 strand를 실행하고 있는지 확인할 수 있어야 한다.&lt;/li&gt;
&lt;li data-line=&quot;15&quot;&gt;strand는 queue를 가지고 handler를 queuing 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-line=&quot;16&quot;&gt;핸들러는 worker 스레드에서만 실행된다.(boost 용어로 io_service::run 실행하는 thread)&lt;/li&gt;
&lt;li data-line=&quot;17&quot;&gt;핸들러 실행 순서는 보장하지 않음.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;구현해야할 주요 메서드는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;21&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;21&quot;&gt;post
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;22&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;22&quot;&gt;추후 실행할 handler를 추가하는 기능. 핸들러를 실행하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-line=&quot;23&quot;&gt;dispatch
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;24&quot;&gt;조건이 맞으면 handler 바로 실행. 아니면, post 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-line=&quot;25&quot;&gt;run
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;26&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;26&quot;&gt;대기중인 handler 실행. public interface 아님.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDf4L/btrKoSXEhkW/ltHplOxvsgWYhRO69Geb2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDf4L/btrKoSXEhkW/ltHplOxvsgWYhRO69Geb2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDf4L/btrKoSXEhkW/ltHplOxvsgWYhRO69Geb2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDf4L%2FbtrKoSXEhkW%2FltHplOxvsgWYhRO69Geb2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;308&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/59IrZ/btrKlaSbnIX/Q2WB9woJJw9VtMpEepsaC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/59IrZ/btrKlaSbnIX/Q2WB9woJJw9VtMpEepsaC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/59IrZ/btrKlaSbnIX/Q2WB9woJJw9VtMpEepsaC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F59IrZ%2FbtrKlaSbnIX%2FQ2WB9woJJw9VtMpEepsaC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;246&quot; data-origin-width=&quot;487&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MAGPx/btrKj0W7fmW/Nxiaq8kmgWuLJoGoJ0fHk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MAGPx/btrKj0W7fmW/Nxiaq8kmgWuLJoGoJ0fHk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MAGPx/btrKj0W7fmW/Nxiaq8kmgWuLJoGoJ0fHk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMAGPx%2FbtrKj0W7fmW%2FNxiaq8kmgWuLJoGoJ0fHk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;518&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-line=&quot;36&quot; data-ke-size=&quot;size16&quot;&gt;사용한 Helper class는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;37&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;37&quot;&gt;&lt;a href=&quot;http://www.crazygaze.com/blog/2016/03/11/callstack-markers-boostasiodetailcall_stack/&quot; data-href=&quot;http://www.crazygaze.com/blog/2016/03/11/callstack-markers-boostasiodetailcall_stack/&quot;&gt;CallStack&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;38&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;38&quot;&gt;현재 콜스택에 마커를 놓고, 현재 스레드에서 함수가 실행되는지 확인할 수 있게 해줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-line=&quot;39&quot;&gt;&lt;a href=&quot;https://www.crazygaze.com/blog/2016/03/13/simple-way-to-shutdown-multiple-consumer-threads/&quot; data-href=&quot;https://www.crazygaze.com/blog/2016/03/13/simple-way-to-shutdown-multiple-consumer-threads/&quot;&gt;WorkQueue&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;40&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;40&quot;&gt;간단한 mutiple producer / consumer queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;42&quot; data-ke-size=&quot;size16&quot;&gt;그리고 monitor 클래스.&lt;br /&gt;Object T에 접근할 때 동기화 기능을 갖게 한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;template &amp;lt;class T&amp;gt;
class Monitor {
private:
    mutable T m_t;
    mutable std::mutex m_mtx;
 
public:
    using Type = T;
    Monitor() {}
    Monitor(T t_) : m_t(std::move(t_)) {}
    template &amp;lt;typename F&amp;gt;
    auto operator()(F f) const -&amp;gt; decltype(f(m_t)) {
        std::lock_guard&amp;lt;std::mutex&amp;gt; hold{m_mtx};
        return f(m_t);
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-line=&quot;64&quot; data-ke-size=&quot;size16&quot;&gt;Strand 구현&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#pragma once
#include &quot;Callstack.h&quot;
#include &quot;Monitor.h&quot;
#include &amp;lt;assert.h&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;functional&amp;gt;
 
//
// A strand serializes handler execution.
// It guarantees the following:
// - No handlers executes concurrently
// - Handlers are only executed from the specified Processor
// - Handler execution order is not guaranteed
//
// Specified Processor must implement the following interface:
//
//  template &amp;lt;typename F&amp;gt; void Processor::push(F w);
//      Add a new work item to the processor. F is a callable convertible
// to std::function&amp;lt;void()&amp;gt;
//
//  bool Processor::canDispatch();
//      Should return true if we are in the Processor's dispatching function in
// the current thread.
//
template &amp;lt;typename Processor&amp;gt;
class Strand {
public:
    Strand(Processor&amp;amp; proc) : m_proc(proc) {}
 
    Strand(const Strand&amp;amp;) = delete;
    Strand&amp;amp; operator=(const Strand&amp;amp;) = delete;
 
    // Executes the handler immediately if all the strand guarantees are met,
    // or posts the handler for later execution if the guarantees are not met
    // from inside this call
    template &amp;lt;typename F&amp;gt;
    void dispatch(F handler) {
        // If we are not currently in the processor dispatching function (in
        // this thread), then we cannot possibly execute the handler here, so
        // enqueue it and bail out
        if (!m_proc.canDispatch()) {
            post(std::move(handler));
            return;
        }
 
        // NOTE: At this point we know we are in a worker thread (because of the
        // check above)
 
        // If we are running the strand in this thread, then we can execute the
        // handler immediately without any other checks, since by design no
        // other threads can be running the strand
        if (runningInThisThread()) {
            handler();
            return;
        }
 
        // At this point we know we are in a worker thread, but not running the
        // strand in this thread.
        // The strand can still be running in another worker thread, so we need
        // to atomically enqueue the handler for the other thread to execute OR
        // mark the strand as running in this thread
        auto trigger = m_data([&amp;amp;](Data&amp;amp; data) {
            if (data.running) {
                data.q.push(std::move(handler));
                return false;
            } else {
                data.running = true;
                return true;
            }
        });
 
        if (trigger) {
            // Add a marker to the callstack, so the handler knows the strand is
            // running in the current thread
            Callstack&amp;lt;Strand&amp;gt;::Context ctx(this);
            handler();
 
            // Run any remaining handlers.
            // At this point we own the strand (It's marked as running in
            // this thread), and we don't release it until the queue is empty.
            // This means any other threads adding handlers to the strand will
            // enqueue them, and they will be executed here.
            run();
        }
    }
 
    // Post an handler for execution and returns immediately.
    // The handler is never executed as part of this call.
    template &amp;lt;typename F&amp;gt;
    void post(F handler) {
        // We atomically enqueue the handler AND check if we need to start the
        // running process.
        bool trigger = m_data([&amp;amp;](Data&amp;amp; data) {
            data.q.push(std::move(handler));
            if (data.running) {
                return false;
            } else {
                data.running = true;
                return true;
            }
        });
 
        // The strand was not running, so trigger a run
        if (trigger) {
            m_proc.push([this] { run(); });
        }
    }
 
    // Checks if we are currently running the strand in this thread
    bool runningInThisThread() {
        return Callstack&amp;lt;Strand&amp;gt;::contains(this) != nullptr;
    }
 
private:
    // Processes any enqueued handlers.
    // This assumes the strand is marked as running.
    // When there are no more handlers, it marks the strand as not running.
    void run() {
        Callstack&amp;lt;Strand&amp;gt;::Context ctx(this);
        while (true) {
            std::function&amp;lt;void()&amp;gt; handler;
            m_data([&amp;amp;](Data&amp;amp; data) {
                assert(data.running);
                if (data.q.size()) {
                    handler = std::move(data.q.front());
                    data.q.pop();
                } else {
                    data.running = false;
                }
            });
 
            if (handler)
                handler();
            else
                return;
        }
    }
 
    struct Data {
        bool running = false;
        std::queue&amp;lt;std::function&amp;lt;void()&amp;gt;&amp;gt; q;
    };
    Monitor&amp;lt;Data&amp;gt; m_data;
    Processor&amp;amp; m_proc;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-line=&quot;213&quot; data-ke-size=&quot;size16&quot;&gt;사용예&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &quot;Strand.h&quot;
#include &quot;WorkQueue.h&quot;
#include &amp;lt;random&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;atomic&amp;gt;
 
int randInRange(int min, int max) {
    std::random_device rd;
    std::mt19937 eng(rd());
    std::uniform_int_distribution&amp;lt;&amp;gt; distr(min, max);
    return distr(eng);
}
 
struct Obj {
    explicit Obj(int n, WorkQueue&amp;amp; wp) : strand(wp) {
        name = &quot;Obj &quot; + std::to_string(n);
    }
 
    void doSomething(int val) {
        printf(&quot;%s : doing %dn&quot;, name.c_str(), val);
    }
    std::string name;
    Strand&amp;lt;WorkQueue&amp;gt; strand;
};
 
void strandSample() {
    WorkQueue workQueue;
    // Start a couple of worker threads
    std::vector&amp;lt;std::thread&amp;gt; workerThreads;
    for (int i = 0; i &amp;lt; 4; i++) {
        workerThreads.push_back(std::thread([&amp;amp;workQueue] { workQueue.run(); }));
    }
 
    // Create a couple of objects that need strands
    std::vector&amp;lt;std::unique_ptr&amp;lt;Obj&amp;gt;&amp;gt; objs;
    for (int i = 0; i &amp;lt; 8; i++) {
        objs.push_back(std::make_unique&amp;lt;Obj&amp;gt;(i, workQueue));
    }
 
    // Counter used by all strands, so we can check if all work was done
    std::atomic&amp;lt;int&amp;gt; doneCount(0);
 
    // Add work to random objects
    const int todo = 20;
    for (int i = 0; i &amp;lt; todo; i++) {
        auto&amp;amp;&amp;amp; obj = objs[randInRange(0, objs.size() - 1)];
        obj-&amp;gt;strand.post([&amp;amp;obj, i, &amp;amp;doneCount] {
            obj-&amp;gt;doSomething(i);
            ++doneCount;
        });
    }
 
    workQueue.stop();
    for (auto&amp;amp;&amp;amp; t : workerThreads) {
        t.join();
    }
 
    assert(doneCount == todo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;%EA%B2%B0%EB%A1%A0&quot; data-line=&quot;277&quot;&gt;결론&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;278&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-line=&quot;278&quot;&gt;명시적 lock이 없어서 코드가 간결해지고, 버그 확률도 줄어든다.&lt;/li&gt;
&lt;li data-line=&quot;279&quot;&gt;blocking 이 적어진다.&lt;/li&gt;
&lt;li data-line=&quot;280&quot;&gt;Cache locality가 좋아진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-line=&quot;282&quot; data-ke-size=&quot;size16&quot;&gt;참고 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.gamedeveloper.com/programming/how-strands-work-and-why-you-should-use-them&quot;&gt;https://www.gamedeveloper.com/programming/how-strands-work-and-why-you-should-use-them&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/C++</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/185</guid>
      <comments>https://dockdocklife.tistory.com/entry/Strand-%EC%A0%95%EB%A6%AC#entry185comment</comments>
      <pubDate>Tue, 23 Aug 2022 14:54:10 +0900</pubDate>
    </item>
    <item>
      <title>부사 더 쓰기</title>
      <link>https://dockdocklife.tistory.com/entry/%EB%B6%80%EC%82%AC-%EB%8D%94-%EC%93%B0%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;definitely&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's definitely better.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's definitely lighter.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That's definitely fake.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;carefully&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I read through the instructions very carefully many times.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I carefully asked him how old he was.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I carefully asked her if she was married.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thoroughly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;do a thorough job&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wow they did a real thorough job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;He thoroughly went over every single detail during the meeting.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;During the meeting, the boss went through every single detail.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;noticeably&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is noticeably thinner and lighter than the last version.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Her condition got noticeably better over time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;My sister was in a car accident a few days ago.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's nothing serious. No one got hurt.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But she's undertandably still quite shaken up by it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;She was arguably one of the best singers in the 1980's.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parallel paring is arguably the hardest driving skill to master.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We're patiently / anxiously / eagerly wating for some good news.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;definitely&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. It's definitely worse.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. It's definitely heavier.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. That's definitely true.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. I think we can definitely go this time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. I would've definetly liked to have some options with source.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. It could definitely been served with a bowl instead of a plate.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;carefully&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. He read through the instrunctions about his car very carefully many times.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. She carefully asked me how old I was.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. He carefully prepared the presentation he was going to show&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. They should read this instruction very carefully.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. People who read carefully read the warning can survied.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. We had carefully our hair dry because if not, we could have snow on our sholders.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thoroughly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Wow He did a real thorough job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Give him big applause for doing a real thorough job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. She thoroughly went over every single detail during the meeting.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. During the meeting, he went through every single line very thoroughly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. we expect she is going to do a real thorough job.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. I read thoughly the instructions as my cat is lying around all the day.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;noticeably&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. This is going to be noticeably thinner and lighter than the last version.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Our relation ship is noticeably better than the last.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. His condition got noticeably better over time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. The weather got noticeably better over time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Almost all the things that was damaged by the heavy rain got noticeably better over time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. This is noticeably more silient than the last thing.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;understandably&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. They was in a car accident a few days ago. It's nothing serious. No one got hurt.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But someone of them are understandably still quite shaken up by it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Some accident come up a few months ago. No one got hurt.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But someone have only lived for a few months, so they are understandably still quite shaked up by it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Sometimes, a false fire alarm rang in the middle of night. I am now quite accustomed it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But at a first few times I am understandably quite shaked up by it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. I got a new car a few days ago. It's noticeably bigger than the last one.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I have only drived for 6 months, so I understandably am little hard to park it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. She had no experience about this job. So, she understandably missed a little detail.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. They are going to be understandably shaken up by this roller coaster.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arguably&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. They are arguably one of the best football players in this time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Parallel parking was arguably the hardest driving skill to master before an auto paking came out.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. A caffe is not arguably good place to study.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Listening a classic music is arguably good for a baby.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. A coffee is arguably good for health.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. The wine is arguably one of the best among the same level line up.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;patiently / anxiously / eargerly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. I am eargerly wating for being promoted this year.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. She is patiently wating for her hersband.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. They are going to be anxiously ready for the news.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. We got anxiously our car fixed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. He was patiently wating for the enemy even though there were many noisy arguments&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. We eagerly wanted the team could participate in the world cup.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://www.youtube.com/watch?v=Lz1wHcWrLt4&amp;amp;t=684s&quot;&gt;부사를 더 써야 합니다   - YouTube&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/영어</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/182</guid>
      <comments>https://dockdocklife.tistory.com/entry/%EB%B6%80%EC%82%AC-%EB%8D%94-%EC%93%B0%EA%B8%B0#entry182comment</comments>
      <pubDate>Mon, 15 Aug 2022 11:57:34 +0900</pubDate>
    </item>
    <item>
      <title>Although, I would've liked, I feel like + could've</title>
      <link>https://dockdocklife.tistory.com/entry/Its-okay-Its-all-right</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;It's okay / It's all right.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although, it's a little slow-paced for me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, it's all text and I really would've liked to see visual aids here and there.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, I feel like it could've been written in plainer language.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I'm finding some parts very difficult to understand.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It was good.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although, it was a little too bland for me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, I would've liked to have some options with the sauce.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And I feel like it could've been served in a bowl instead of a plate.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I thinkg it would've been easier to eat.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It went well / okay / all right.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although, it was a little awkward for me when I was signing the contract.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I really would've liked to have a day or two to review it more thoroughly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And I feel like the meating could've been held a little earlier in the day.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. It's okay. Althogh, it's a little slow-paced for me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, the dialogue sound is hard to listen because the background sound is higher than this.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I really would've liked to see some subtitles when the war scene was playing.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, I feel like it could been written in plainer korean. I found some parts very difficult to understand.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. It was good. Although, it was a little too blend for me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, I would've liked to have some options with the soup.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, I feel like it could've been served with a spoon instead of just a folk.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think it would've been easier to pick the food.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. It went well. Although it was a little awkward for me when I was explaining the contract.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I really would've liked to have a one or two day to review it more thoroughly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, I think the next meeting could have scheduled a little earlier than the day we talked.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. I think it is going to be well.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although there is a concern about we don't expect.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plus, I would have liked to have a day or two day to prepare thoroughly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We could have expected more perfect result if we had extra days.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. It is good. Although it is a little too bland for me.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I really would liked it has a slightly stronger flavor than this.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And I feel like it could have been some sparklings in the one.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It could be more popular than now.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. It was good. Although there was a enormous traffic jam in the morning.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I could have been late in the office.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I would have liked to be reported about a current traffic environment.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If it is expected to be a traffic jam, I could choose other transport platforms.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://www.youtube.com/watch?v=NwlFMatCR-c&quot;&gt;영어 말하기 연습 가이드   &quot;좋았지만 (이런저런 것)이 아쉬웠어.&quot; - YouTube&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/영어</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/181</guid>
      <comments>https://dockdocklife.tistory.com/entry/Its-okay-Its-all-right#entry181comment</comments>
      <pubDate>Mon, 15 Aug 2022 10:12:38 +0900</pubDate>
    </item>
    <item>
      <title>Having met, having lived, having been</title>
      <link>https://dockdocklife.tistory.com/entry/Having-met-having-lived-having-been</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;I moved to my current place in October last year.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I had a lot of initial concerns back then because, well you know, there are certain things about a place you just can't know for sure until you actually start living there, like neighbors.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But now having lived there for almost 6 months, I can pretty confidently say that i'm very satisfied with the place.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I started teaching English straight out of a school in 2008 and haven't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So, I have been doing this for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Having met hudres if not thousand of students throughout my career,&amp;nbsp; I feel like I have a pretty good understanding of what the students need, what their weekness are and therefore what i need to do for them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;===========================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I moved to my current place in October last year&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. I moved to my current place after I have married.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. I'm going to move to next to my current place late this year.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. She moved to my current place in October because we have married.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. They are going to move their place two years later.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. I have no money as I moved my home just before.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. As long as it's not an emergency, we are going to move our place.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I had a lot of inital concerns back then because, well you know, there are certain things about a place you just can't konw for sure until you actually start living there, like neighbors.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. It could be that she had a lot of initial concerns back then because, well you know, there are certain things about a place you just can't know for sure until you actually start living there, like neighbors.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. I had a lot of concerns back then because, well you know, there are certain things about a company you just can't know for sure until you actuall start working for it, like team members.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. She has a lot of concerns about the presentation she is goint to do. because she worrys about how the audience think the presentation is.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. They had a lot of concerns back then, well you know there are certain thins about a team work you just don't know for sure until you actaully start doing team work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. We are going to have a few concerns slight moments layer well you know there are certain things about a task we have to do.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. We had a lot of concerns back then well you know, there are certain things about a economy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But now having lived there for almost 6 months, I can pretty confidently say that I'm very satisfied with the place.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. Having lived my home for almost 6 months, I can pretty confidently say that Im very satisfied with the place.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. Having wored for my company for almost half a year, I can pretty confidently say that I'm satisfied with my company.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. Having lived with for almost 6 months, I can pretty confidently say that I'm satified with my life with my wife.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. Having used the laptop almost 1 year, I can pretty confidently say that I'm satisfied with this one.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. Having had a lot of concerns back then, I think it is little useless.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. Having finished the task, I don't know why I made so many mistakes.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I started teaching English straight out of a school in 2008 and haven't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. I started working straight out of a school in 2016 and haven't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. I started leaning the computer science straight out of a school in 2016 and haven't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. She started preparing a dinner right after she got to a home and hasn't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. He is going to start speaking to us straing out of the meeting and is not gonna stop it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. I could have started studying straing out of a office but instead I had watched videos on the Youtube.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. I started running right after I got to the home and haven't stopped ever since.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So, I have been doing this for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. So, I have been software engineer for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. So, you are going to doing this for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. So, She has been doing this for a farily long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. So, They have been living together for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. So, this has been happening for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. So, I have been thinking for a fairly long time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Having met hundreds if not thousand of students throught my carrer, I feel like I have a pretty good understanding of what the students need, what their weekness are and therefore what I need to do for them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 1. Having done this hudreds times if not thousand times throught all a week, I feel like I have a pretty good understanding of what this is, what this needs and therefore what i need to do for this.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 2. Having taken dozens if not hundred of pictures throught all day, I feel like I have pretty good understanding how to take a picture, what i need to do for a good picture and therefore how nicely I provide the service.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 3. Having written tens if not dozen of sentences throught the study time, I feel like I have pretty good understaing how to express, what words is needed and therefore what I need to speak better sentences.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 4. Having cleaned tens if not dozens throught the time we have lived together I feel like I have pretty good understaing how nicely to clean, what she is satisfied with and therefore what I need to do for my wife and my home.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 5. Having had times together hudreds if not thousand of days throught days we have lived together, I feel like I have pretty good understaing of what she likes dislikes and therefore what I need to do for making her happy.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 6. Having drunken tens if not hudred of wines you know I feel like i have pretty good understanding of what the good wine is, what are good foods having together with.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 : &lt;a href=&quot;https://www.youtube.com/watch?v=0E1ndEJ_wa8&quot;&gt;(3) having met, having lived, having been 특이하지만 자주 쓰이는 분사구문 - YouTube&lt;/a&gt;&lt;/p&gt;</description>
      <category>똑똑한 개발/영어</category>
      <author>성댕쓰</author>
      <guid isPermaLink="true">https://dockdocklife.tistory.com/180</guid>
      <comments>https://dockdocklife.tistory.com/entry/Having-met-having-lived-having-been#entry180comment</comments>
      <pubDate>Sun, 7 Aug 2022 11:47:09 +0900</pubDate>
    </item>
  </channel>
</rss>