👉🏻 c++의 Asio(또는 Bootst.asio) 라이브러리를 이해하기 위한 코드입니다.
This is code to understand the C++ Asio (or Bootst.asio) library.
👉🏻 아래의 코드는 이전에 업로드한 mini_mediaserver_1 의 일부로직을 사용합니다.
The code below uses some of the logic from the previously uploaded mini_mediaserver_1.
👉🏻 테스트 환경은 MacOS입니다.
The test environment is MacOS.
👉🏻 mini_mediaserver_1은 standalone방식의 asio를 사용합니다.
mini_mediaserver_1 uses standalone asio.
👉🏻 여기서는 Boosta.asio를사용합니다.
Here, we use Boosta.asio.
👉🏻 설치 및 사용 / Install and Usage
✔️ Boost.Asio 전체 설치 / full installation
— 설치 / Installatioon
# macOS brew - boost.asio
brew install boost
— 사용 / Usage
#include "boost/asio"
✔️ Asio Standalone
— 설치 / Installation
# macOS brew - asio standalone
brew install asio
— 사용 / Usage
#include "asio.hpp"
👉🏻 코드 / Code
✔️코드 설명 / Code Description
— 함수 실행 흐름 / Function execution flow
main() -> Server Class- > do_accept() -> Session Class -> start { do_read {do_write()}}
— 소켓,접속정보 공유 / Socket, connection information sharing
1.소켓은 복사 할 수 없기 때문에 소유권 이동을 합니다.
Since sockets cannot be copied, ownership is transferred.
std::make_shared<Session>(std::move(socket))->start();
2.접속 정보를 공유하기 위해 다음 코드를 사용합니다.
Use the following code to share connection information.
- make_shared는 하나만 공유할 수 있는 포인터를 만듭니다.
make_shared creates a pointer that can be shared by only one person.
std::make_shared<Session>(std::move(socket))->start();
- enable_shared_from_this를 상속받으면 shared_from_this(); 함수를 사용 할 수 있습니다.
If you inherit from enable_shared_from_this, you can use the shared_from_this(); function.
public std::enable_shared_from_this<Session>
- this(Session class) 정보가 사라질 수 있어서 안전한 연결을 위해 self에 Session Class 정보를 복하합니다.
Since the information in this (Session class) can be lost, the Session Class information is copied to self for a secure connection.
auto self = shared_from_this();
— echo
- 아래의 람다 코드에서 소켓에서 받은 data_의 내용을 출력하고 다시 소켓에 데이터를 작성합니다.
In the lambda code below, the contents of data_ received from the socket are printed, and data is written back to the socket.
[this, self](boost::system::error_code ec, size_t len) {
if (!ec) {
std::cout << "받음/Receive: " << std::string(data_, len) << "\n";
do_write(len); // 에코 / echo
}
});
✔️전체 코드 / Full Cdoe (tcpecho.cpp)
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
// --
/*
- std::make_shared<Session>(std::move(socket)) :
한 곳만 포인터 공유설정 / Pointer sharing settings for only one location
- : public std::enable_shared_from_this<Session> :
shared_from_this(); 사용을 위해 상속
Inheritance for using shared_from_this();
- shared_from_this :
class Session 객체를 복사해서 저장
Copy and save the class Session object
** this가 소멸될 수 있어서 안전한 세션연결 유지를 위해 Session클래스를 하나 더 복사함
Since 'this' can be destroyed,
an additional copy of the Session class is made to maintain a safe session connection. **
*/
class Session : public std::enable_shared_from_this<Session> {
tcp::socket socket_;
char data_[1024];
public:
Session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() {
do_read();
}
private:
void do_read() {
// 람다 살아있는 동안 Session 안 죽음 / The Session does not die as long as Lambda is alive
// 이 클래스 객체를 복사해서 저장 / Copy and save this class object
auto self = shared_from_this();
/*
* async_read_some(buffer(data_), 람다/rambda)
* 1) 데이터 오면 data_에 채워놓고 람다 실행 예약만 한다.
* When data arrives, fill it into data_ and just schedule the Lambda execution.
*
* 2) OS가 소켓에 데이터 들어오는 거 감지 → data_ 배열에 len바이트만큼 복사해 둔다.
* The OS detects incoming data in the socket and copies len bytes into the data_ array.
*
* 3) 복사가 끝났으면 람다 실행
* Run Lambda once copying is finished
*
* 4) 이미 data_에 들어와 있는 변수를 람다에서 사용
* Using variables already in data_ in Lambda
*/
socket_.async_read_some(boost::asio::buffer(data_),
[this, self](boost::system::error_code ec, size_t len) {
if (!ec) {
std::cout << "받음/Receive: " << std::string(data_, len) << "\n";
do_write(len); // 에코 / echo
}
});
}
void do_write(size_t len) {
auto self = shared_from_this();
boost::asio::async_write(socket_, boost::asio::buffer(data_, len),
[this, self](boost::system::error_code ec, size_t) {
if (!ec) do_read(); // 읽기 / read
});
}
};
// --
class Server {
// socket() + bind() + listen()수행 / Execute socket() + bind() + listen()
tcp::acceptor acceptor_; // private
public:
Server(boost::asio::io_context& io, short port)
: acceptor_(io, tcp::endpoint(tcp::v4(), port)) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
// 람다함수 / Lambda function
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
// 에러가 없으면 소켓을 세션에 넘기고 start 호출, 소켓은 복사 안됨
// If there are no errors, pass the socket to the session and call start;
// the socket is not copied
std::make_shared<Session>(std::move(socket))->start();
}
// 다음 클라이언트 계속 받음
// Continue accepting next client
//
// 무한루프 / infinite loop
do_accept();
});
}
};
int main() {
try {
boost::asio::io_context io;
Server s(io, 12345);
std::cout << "서버 시작. 포트 12345 / Start server. Port 12345\n";
io.run();
} catch (std::exception& e) {
std::cerr << "에러/Error: " << e.what() << "\n";
}
return 0;
}
👉🏻컴파일 / Compiling
— boot라이브러리의 경로를 포함합니다.
Includes the path to the boot library.
g++ tcpecho.cpp -o server -std=c++17 -pthread \
-I/opt/homebrew/include \
-L/opt/homebrew/lib \
-lboost_system
👉🏻실행 / Run
✔️ 터미널을 두개 열어서 하나는 서버 하나는 클라이언트를 실행합니다.
Open two terminals and run the server and the client in one.
✔️nc(netcat)는 TCP/UDP 통신 테스트 도구입니다.
nc(netcat) is a TCP/UDP communication test tool.
✔️ 클라이언트는 nc localhost 12345를 실행합니다.
The client runs nc localhost 12345.
# server
./server
# client
nc localhost 12345
👉🏻실행 결과 / Execution result
