검색결과 리스트
Programming에 해당되는 글 32건
- 2016.04.03 Daytime.3 - An asynchronous TCP daytime server
- 2016.03.09 Learn You Some Erlang 1
- 2015.05.20 IO Type
글
Daytime.3 - An asynchronous TCP daytime server
다음에 기반함
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/tutorial/tutdaytime3.html
main 함수
int main() { try {
먼저, 클라이언트의 연결요청을 받아들일 객체가 필요하다. io_service 객체는 소켓과 같은 I/O 서비스를 제공하므로, 이를 이용하자.
boost::asio::io_service io_service; tcp_server server(io_service);
io_service 객체를 run 하여 비동기 연산을 수행하도록 하자.
io_service.run(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }
tcp_server 클래스
class tcp_server { public:
생성자에서 포트 13의 요청을 listen 하도록 초기화 해주자.
tcp_server(boost::asio::io_service& io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13)) { start_accept(); } private:
start_accept() 함수에서 소켓을 생성하고 async accept 를 시작하여 새로운 연결을 받아들이자.
void start_accept() { tcp_connection::pointer new_connection = tcp_connection::create(acceptor_.get_io_service()); acceptor_.async_accept(new_connection->socket(), boost::bind(&tcp_server::handle_accept, this, new_connection, boost::asio::placeholders::error)); }
handle_accept 는 start_accept 함수가 종료되어 accept 연산이 개시될 때 호출된다. 이 함수에서 클라이언트의 요청을 처리하고, start_accept() 를 다시 호출하여 다음 연결 요청을 받을 수 있도록 하자.
void handle_accept(tcp_connection::pointer new_connection, const boost::system::error_code& error) { if (!error) { new_connection->start(); } start_accept(); }
tcp_connection 클래스
shared_ptr 과 enable_shared_from_this 를 활용하여 관련한 연산이 수행되는 한 tcp_connection 객체가 소멸되지 않도록 유지하자.
class tcp_connection : public boost::enable_shared_from_this<tcp_connection> { public: typedef boost::shared_ptr<tcp_connection> pointer; static pointer create(boost::asio::io_service& io_service) { return pointer(new tcp_connection(io_service)); } tcp::socket& socket() { return socket_; }
start() 함수에서는 boost::asio::async_write() 함수를 활용하여 클라이언트로 데이터를 전송한다.
ip::tcp::socket::async_write_some() 대신 async_write 를 사용하여 전체 데이터 블록이 전송될 수 있도록 했음을 확인하자.
void start() {
비동기로 메시지를 전송하는 것이 완료될 때까지 데이터가 유효함을 보장하기 위해 클래스 멤버로 데이터를 보관 할 것이다.
message_ = make_daytime_string();
boost::bind() 를 활용하여 비동기 연산을 수행할때, 핸들러의 parameters 와 호출부의 arguments 가 일치하도록 주의한다.
placeholder 들 (boost::asio::placeholders::error / boost::asio::placeholders::bytes_transferred) 이 handle_write() 에서 사용되지 않기때문에, 실수로 제거될 수 있다.
boost::asio::async_write(socket_, boost::asio::buffer(message_), boost::bind(&tcp_connection::handle_write, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
이 연결에 필요한 기타 추가 작업들은 이제 handle_write() 에서 모두 처리하면 된다.
} private: tcp_connection(boost::asio::io_service& io_service) : socket_(io_service) { } void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) { } tcp::socket socket_; std::string message_; };
불필요한 핸들러 인자 제거
우리의 예제에서, error 와 bytes_transsferred 인자는 handle_write 함수 몸체에서 불필요하다. 이러한 경우, 함수에서 이를 제거하여 다음과 같은 시그니쳐를 갖게 할 수 있다.
void handle_write() { }
이 함수를 호출부인 boost::asio::async_write() 를 호출할때에는 다음과 같이 수정하면 된다.
boost::asio::async_write(socket_, boost::asio::buffer(message_), boost::bind(&tcp_connection::handle_write, shared_from_this()));
정리
1. main 함수
* tcp_server 에 io_service 를 전달하여 생성하고, tcp_server 클래스를 initiate 한다.
* 메인 스레드에서 tcp_server 에서 사용하는 io_service 의 run() 함수 호출
2. tcp_server 클래스
* boost::asio::ip::tcp::begin_accept 를 활용하여 클라이언트의 연결을 받고, tcp_connection 객체를 생성한다.
* connection 의 start 함수를 호출한다.
* 완료 된 후, 다음 연결을 받기 위해 다시 accept 를 시작한다.
3. tcp_connection 클래스
* enable_shared_from_this 를 활용하여 이 객체에 대한 연산이 수행되는 경우 소멸되지 않도록 보장한다.
* 클라이언트로 전달하는 데이터가 소멸되지 않게 하기 위해, 클래스의 멤버로 데이터를 보전한다.
* 전송 데이터가 분리되지 않게 하기 위해 async_write() 함수를 활용
* 사용하지 않는 place_holder 를 제거하여 핸들러 인자의 시그니쳐를 단순히 가져 갈 수도 있다 (async_write)
Full source code
--
#pragma once #include#include #include #include "boost/shared_ptr.hpp" #include "boost/asio.hpp" #include "boost/bind.hpp" #include "boost/enable_shared_from_this.hpp" //* enable_shared_from_this 를 활용하여 이 객체에 대한 연산이 수행되는 경우 소멸되지 않도록 보장한다. //* 클라이언트로 전달하는 데이터가 소멸되지 않게 하기 위해, 클래스의 멤버로 데이터를 보전한다. //* 전송 데이터가 분리되지 않게 하기 위해 async_write() 함수를 활용 //* 사용하지 않는 place_holder 를 제거하여 핸들러 인자의 시그니쳐를 단순히 가져 갈 수도 있다(async_write) class TCPConnection : public boost::enable_shared_from_this { public: typedef boost::shared_ptr pointer; static pointer create(boost::asio::io_service& io) { return pointer(new TCPConnection(io)); } boost::asio::ip::tcp::socket& socket() { return socket_; } void start() { message_ = make_daytime_string(); boost::asio::async_write(socket_, boost::asio::buffer(message_), boost::bind(&TCPConnection::handle_write, shared_from_this())); } private: TCPConnection(boost::asio::io_service& io) : socket_(io) { } void handle_write() { std::cout << message_ << ", write_done" << std::endl; } std::string make_daytime_string() { std::time_t now = time(0); return ctime(&now); } boost::asio::ip::tcp::socket socket_; std::string message_; }; //* boost::asio::ip::tcp::begin_accept 를 활용하여 클라이언트의 연결을 받고, tcp_connection 객체를 생성한다. //* connection 의 start() 함수를 호출한다. //* 완료 된 후, 다음 연결을 받기 위해 다시 accept 를 시작한다. class TCPServer { public: TCPServer(boost::asio::io_service& io) : acceptor_(io, boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), 13)) { start_accept(); } private: void start_accept() { TCPConnection::pointer new_connection = TCPConnection::create(acceptor_.get_io_service()); // async_accept(socket, ACCEPT_HANDLER); acceptor_.async_accept(new_connection->socket(), boost::bind(&TCPServer::handle_accept, this, new_connection, boost::asio::placeholders::error)); } void handle_accept(TCPConnection::pointer new_connection, const boost::system::error_code& error) { if (!error) { new_connection->start(); } start_accept(); } boost::asio::ip::tcp::acceptor acceptor_; }; //* tcp_server 에 io_service 를 전달하여 생성하고, tcp_server 클래스를 initiate 한다. //* 메인 스레드에서 tcp_server 에서 사용하는 io_service 의 run() 함수 호출 void server_run() { try { boost::asio::io_service io_service; TCPServer server(io_service); io_service.run(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } }
'Programming > Boost asio' 카테고리의 다른 글
Daytime.2 - A synchronous TCP daytime server (0) | 2015.04.19 |
---|---|
Daytime.1 - A synchronous TCP daytime client (0) | 2015.04.13 |
Tutorial 5. Synchronising handlers in multithreaded programs (0) | 2015.04.11 |
Tutorial 4. member function handler (0) | 2015.03.17 |
Tutorial 3. Binding arguments to a handler (0) | 2015.03.15 |
설정
트랙백
댓글
글
Learn You Some Erlang
몰랐는데,
learn you ~
시리즈북이 원래 있었나보다. (하스켈도 있다고 그러고. )
근래에 몇달째 functional programming 과 Rx 쪽으로 스터디가 집중되다보니,
말이 펑셔널 펑셔널 하게 짠다지, 좀 잘해보려면 공부를 많이 해야할 것 같아서 .. (현업에서 쓰고있는 부분은 없으니... )
learn You some erlang
http://learnyousomeerlang.com/
을 팀내에서 세미나 형태로 공유하고 있다. - 라고 쓰고 그냥 팀분들께 하는 사기극일지도..-
내용을 잘 정리해서 블로깅을 하거나, 아예 제대로 ? 한글로 번역 - 해서 출판하고 싶은 욕망이 샘솟긴 하는데..
(출판할 수 있을까 ? - 이 끝없는 명예욕....)
일단은 급한대로 한주 한주 세미나를 진행하는 것으로 만족.
다음 github repo 에 매주 발표자료를 업로드 하고 있다.
https://github.com/nolleh/learnyouerlang
다음은 1주차 내용.
https://github.com/nolleh/learnyouerlang/blob/master/week1/week1.pdf
뭔가....첨엔 뭐이렇게 문법이 직관적이지가 않아.. 라고 했다가..
6주째 자료를 만드는 지금은
얼랭 짱짱맨인듯...
설정
트랙백
댓글
글
IO Type
IO
Introduction to IO
IO a
사이드 이펙트를 수행하고, 그 결과로 type a 의 value 를 리턴하는 abstract 타입을 고려할 수 있다.
statements 는 보통 사이드 이펙트를 통해 대부분 communicate 된다. I/O 모나드를 소개함으로써 하스켈에서 statement/action 의 개념을 소개한다. 그러나 이 개념 또한 expression 의 일환이라, 결합이 가능하다. (composition)
IO Char
어떤 사이드이펙트를 수행하고 결과로 some character 를 리턴
IO ()
이 imperative type 에서도 가장 imperative 한 것이 IO () side effect 만 수행함
getChar :: IO Char
getChar :: () -> Char -- () -> something means trying to simulate laziness
putChar :: Char -> IO ()
return :: a -> IO a -- 어떤것도 하지 않고 즉시 리턴
a :: IO (Char, Char)
a = do x <- getChar
getChar
y <- getChar
return (x,y)
standard input 으로 부터 3 문자를 읽어 첫번째, 세번째를 페어로 리턴
오른쪽에는 t 의 IO 가 위치하고 왼쪽에는 t 가 위치하여 두 타입이 다르므로, assign 연산자 대신 <- 를 사용하는 것.
getLine :: IO String
getLine = do x <- getChar
if x == '\n' then
return []
else
do xs <- getLine
return (x:xs)
이번엔 standard output 에 string 을 print 하는 IO 메서드
putStr :: String -> IO ()
putStr [] = return ()
putStr (x:xs) = do putChar x
pubStr xs
putStrLn :: String -> IO ()
putStrLn xs = do putStr xs
putChar '\n'
imperative code 나 I/O 코드를 다른 함수들과 사용할 수 있다는 데 그 강점이 있다. 특히 String 은 그 자체로 리스트이기때문에, 리스트에 사용되는 함수는 위와같은 방식으로의 활용도가 높다.
strlen :: IO ()
strlen = do putStr "Enter a string: "
xs <- getLine
putStr "The string has "
putStr (show (length xs))
putStrLn " characters"
pure code 를 작성하여 impure code 에 embeded 한 예라 할 수 있음.
'Programming > Haskell' 카테고리의 다른 글
Parser in Haskell (0) | 2015.05.18 |
---|---|
Parser 의 bind (0) | 2015.05.15 |