검색결과 리스트
글
C++에서 Boost ASIO를 사용하여 TCP 비동기(Unblocked) 통신 프로그래밍
공대생의 팁
2019. 3. 7. 23:04
C++에서 소켓 통신을 할 때 동기식(Blocked, sync)과 비동기식(Unblocked, async)방식으로 TCP 통신을 구현합니다. 그러나 Java에 비교하였을 때 C에서 기본으로 제공하는 socket 프로그래밍을 사용하는 것은 여간 불편한 일이 아닙니다.
이러한 환경에서 Boost 라이브러리에서 제공하는 ASIO 소켓 통신 프로그램을 사용하게 되면 이전보다 코딩 환경이 매우 쾌적함을 경험할 수 있습니다.
이번 포스팅에서는 Boost 라이브러리에서 제공하는 ASIO를 사용하여 Unblock(Asynchronization)방식의 소켓 통신 프로그램을 구현해 보았습니다.
프로그램을 실행하게 되었을 때 Socket에 IP주소와 Port 번호를 등록하고 이를 io_service(1.66 버전 이후에서는 io_context)에 이 때 handle_connect, handle_read, handle_write와 같은 handler 또한 함께 등록합니다. 이 때 등록된 handler는 server 혹은 client에서 실행되었을 때 바로 handler를 실행하는 방법으로 어느 한 쪽으로부터 데이터 전달을 기다리지 않고 있다가 요청이 들어왔을 때 실행하는 것이지요.
시작하기에 앞서 Ubuntu 환경에서 다음과 같이 Boost 라이브러리를 설치합니다.
$ sudo apt install libboost-all-dev
자세한 내용은 소스코드의 주석을 통해 설명을 달아보았습니다.
server.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | #include <cstdlib> #include <iostream> #include <boost/bind.hpp> #include <boost/asio.hpp> using boost::asio::ip::tcp; using namespace std; class session { public: session(boost::asio::io_service& io_service) //boost 1.66이후 (Ubuntu 18.10 이후) 버전의 경우 io_context를 사용 //session(boost::asio::io_context& io_service) : socket_(io_service) { } tcp::socket& socket() { return socket_; } void start() { //client로부터 연결됨 cout << "connected" << endl; //client로부터 비동기 read 실행 socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } private: void handle_read(const boost::system::error_code& error, size_t bytes_transferred) { if (!error) { cout << data_ << endl; } else { delete this; } } tcp::socket socket_; enum { max_length = 1024 }; char data_[max_length]; }; class server { public: server(boost::asio::io_service& io_service, short port) //boost 1.66이후 (Ubuntu 18.10 이후) 버전의 경우 io_context를 사용 //server(boost::asio::io_context& io_service, short port) : io_service_(io_service), //PORT 번호 등록 acceptor_(io_service, tcp::endpoint(tcp::v4(), port)) { start_accept(); } private: void start_accept() { session* new_session = new session(io_service_); //client로부터 접속될 때 까지 대기한다. acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error)); } //client로부터 접속이 되었을 때 해당 handler 함수를 실행한다. void handle_accept(session* new_session, const boost::system::error_code& error) { if (!error) { new_session->start(); } else { delete new_session; } //client로부터 접속이 끊겼을 대 다시 대기한다. start_accept(); } boost::asio::io_service& io_service_; //boost 1.66이후 (Ubuntu 18.10 이후) 버전의 경우 io_context를 사용 //boost::asio::io_context &io_service_; tcp::acceptor acceptor_; }; int main(int argc, char* argv[]) { try { if (argc != 2) { std::cerr << "Usage: async_tcp_echo_server <port>\n"; return 1; } boost::asio::io_service io_service; //boost 1.66이후 (Ubuntu 18.10 이후) 버전의 경우 io_context를 사용 //boost::asio::io_context io_service; server s(io_service, atoi(argv[1])); //asio 통신을 시작한다. io_service.run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } return 0; } | cs |
client.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #include<cstdlib> #include<cstring> #include<iostream> #include<boost/asio.hpp> #include<boost/bind.hpp> using boost::asio::ip::tcp; using namespace std; enum { max_length = 1024 }; class client { public: client(boost::asio::io_service& io_context, //client(boost::asio::io_context& io_context, const string& host, const string& port) : socket_(io_context) { boost::asio::ip::tcp::resolver resolver(io_context); boost::asio::ip::tcp::resolver::query query(host, port); boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); //비동기(Unblock) 상태로 서버와 접속한다. boost::asio::async_connect(socket_, endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error)); } void handle_connect(const boost::system::error_code& e){ if(!e){ cout << "Connected!" << endl; string msg = "Hello! Server!"; //Server로부터 비동기 write를 시도한다. boost::asio::async_write(socket_, boost::asio::buffer(msg, msg.length()), boost::bind(&client::handle_write,this,boost::asio::placeholders::error)); } } void handle_write(const boost::system::error_code& e){ if(!e){ cout << "Done!" << endl; } } private: tcp::socket socket_; char data_[max_length]; }; int main(int argc, char* argv[]) { try { // Check command line arguments. if (argc != 3) { std::cerr << "Usage: client <host> <port>" << std::endl; return 1; } boost::asio::io_service io_context; //boost::asio:io_context io_context; client c(io_context, argv[1], argv[2]); io_context.run(); } catch (std::exception& e) { cerr << e.what() << endl; } return 0; } | cs |
CMakeLists.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | cmake_minimum_required(VERSION 3.0) project(asio_async) find_package(Boost REQUIRED system) find_package(Threads) include_directories(${Boost_INCLUDE_DIR}) add_executable(client client.cpp ) add_executable(server server.cpp ) target_link_libraries(client ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(server ${Boost_LIBRARIES} ) | cs |
Linux 환경에서 위의 3개의 소스코드를 한 폴더에 넣으신 후 다음과 같이 실행합니다.
$ mkdir build
$ cd build
$ cmake ..
$ make
위와 같은 과정을 거치면 client와 server라는 실행파일이 build 폴더 안에 생성됩니다. 이 두 프로그램을 실행하면 다음과 같은 결과가 나옵니다.
1 2 3 4 | $ ./server 2580 Connected! Hello! Server! | cs |
1 2 3 4 | ./client 127.0.0.1 2580 Connected! Done! | cs |
관련 자료
300x250
'공대생의 팁' 카테고리의 다른 글
LSD-SLAM에서 Semi-Dense의 의미 (1) | 2019.03.19 |
---|---|
C++ 클래스 객체를 stream으로 통신 및 전달방법 - Boost Serialization(2) [Class를 TCP 소켓 통신으로 전송] (0) | 2019.03.08 |
https 차단을 우회하는 방법 - GoodbyeDPI [2019.02.13] (3) | 2019.02.13 |
Co-visibility graph(공가시성 그래프) (0) | 2019.01.22 |
Pixel Intensity(화소 강도) (2) | 2019.01.13 |