http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/examples.html
http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/example/http/server4/coroutine.hpp
일단 링크만 적어둔다.
추가.
http://www.crystalclearsoftware.com/soc/coroutine/
헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤
2010년 1월 21일 목요일
boost asio 를 이용 serialport 로 테스트용 바이트시퀀스를 줄줄이 싸봤다.
보드쪽에서 시리얼 읽는게 좀 이상하길래 만들어본 테스트 코드.
처음엔 haskell 로 짰는데( serialport 라고, haskell 에서 win32 시리얼 포트를 지원하는 라이브러리가 나온게 본기억이 나서 ) ㅋㅋㅋ 잘 안되더라. send 가 묘하게 느리던데.. 업무상 필요한 작업이라 삽질 그만두고 바로 C++ 로 짜버렸다.
테스트한 시나리오가.. C++ 로 0x00 부터 0xFF 를 줄기차게 날리고 보드쪽에서 매번 한바이트읽을때마다 기존 값하고 1 차이나는지 비교한.. 단순히 한방향 통신만 하는 코드라서 시리얼포트에서 읽거나 하는 코드는 들어있지 않다.
뭐 사실 그냥 asio 사용하고 다를거하나 없고 set_option 의 사용법 정도나 봐두면 되겠다.
처음엔 haskell 로 짰는데( serialport 라고, haskell 에서 win32 시리얼 포트를 지원하는 라이브러리가 나온게 본기억이 나서 ) ㅋㅋㅋ 잘 안되더라. send 가 묘하게 느리던데.. 업무상 필요한 작업이라 삽질 그만두고 바로 C++ 로 짜버렸다.
테스트한 시나리오가.. C++ 로 0x00 부터 0xFF 를 줄기차게 날리고 보드쪽에서 매번 한바이트읽을때마다 기존 값하고 1 차이나는지 비교한.. 단순히 한방향 통신만 하는 코드라서 시리얼포트에서 읽거나 하는 코드는 들어있지 않다.
뭐 사실 그냥 asio 사용하고 다를거하나 없고 set_option 의 사용법 정도나 봐두면 되겠다.
#include <boost/asio.hpp>
#include <exception>
#include <iostream>
using namespace std;
int main()
{
try
{
boost::asio::io_service io_service;
// 시리얼포트 열고
boost::asio::serial_port s(io_service, "COM4");
boost::asio::serial_port::baud_rate baud_rate(9600);
s.set_option(baud_rate);
boost::asio::deadline_timer t(io_service);
unsigned char buf[1] = {0};
while(true)
{
// 한바이트씩 보내자
s.write_some(boost::asio::buffer(buf, sizeof(buf)));
cout << static_cast<unsigned int>(buf[0]) << endl;
// 0x00 부터 0xFF 까지 보내면 되겠지.
buf[0]++;
// 잠시 쉬자.
t.expires_from_now(boost::posix_time::milliseconds(5));
t.wait();
}
io_service.run();
}
catch(exception& e)
{
cerr << e.what() << endl;
}
}
2009년 4월 14일 화요일
boost::asio 로 만들어본 간단한 콘솔 서버 ( async_read_until 사용예 )
서버를 짜다보면 간단한 제어 인터페이스를 노출할 경우가 많은데 보통 웹을 붙이거나 shell 을 흉내낸다. 이하는 asio 로 간단히 shell 흉내를 내는 코드를 만들어본것.
그냥 시간이 남아 간단히 만들어본건데 async_read_until 때문에 조금 고생했다.
이놈이 받는 버퍼는 다른 async 류 함수들과 달리 streambuf 더라.
나는 평소 C++ 을 쓰더라도 stream 쪽은 전혀 쓰질 않아서 이쪽은 하나도 모르는데.. 그래서 좀 애먹었다. 급한대로 streambuf 로부터 한글자씩 꺼내서 처리해봤는데 이건 좀 아닌거 같고 좀더 나은 방법을 뒤져봐야겠다.
이하는 main 을 담고있는 코드.
한줄을 받아서 한줄을 내놓는 최소한의 로직만 담고있는 클래스 echo 를 정의하고 이놈으로 console<echo> 인스턴스를 만들어 io_service 에 붙였다. 당연히 그냥 asio 공부하려고 만들어본 코드니 제구실하기엔 부족한 코드.
console<T> 는 그냥 acceptor 라고 보면 된다. 특정 포트로 tcp 받아서 session<T> 를 까주는 놈. session<T> 는.. 결국 session<echo> 로 인스턴스가 만들어지는데 echo 가 가진 함수를 부르기 위한 소켓관련 작업들을 해주는 놈이다.
좀더 뒤져서 asio::streambuf 에서 값을 꺼내는 방법을 하나 더 찾았다.
asio::const_buffer data = buf_.data();
const char* b = asio::buffer_cast<const char*>(data);
const char* e = b + readed;
뭐 대략 이런식인데 이것도 통쾌한 방법은 아닌거 같다. 문서상 그리고 메일링에서도 istream 을 통해 꺼내오는것을 권장하는거 같은데.. iostream 쪽 코드는 영 손대기 그렇구만..
그리고 이런식으로 뽑아쓸경우 consume 으로 버퍼 비워주는것도 잊지말자.
추가.
ReadHandler 같은경우 error 와 readed 를 인자로 받는데 나는 error 나면 readed 무시하고 바로 에러를 내도록 했지만 경우에 따라 readed 를 먼저 처리하고 error 확인해야 할때도 있을거 같은데.. 이건 나중에 asio 를 정말 쓰게되면 다시 만날 문제겠지.
그냥 시간이 남아 간단히 만들어본건데 async_read_until 때문에 조금 고생했다.
이놈이 받는 버퍼는 다른 async 류 함수들과 달리 streambuf 더라.
나는 평소 C++ 을 쓰더라도 stream 쪽은 전혀 쓰질 않아서 이쪽은 하나도 모르는데.. 그래서 좀 애먹었다. 급한대로 streambuf 로부터 한글자씩 꺼내서 처리해봤는데 이건 좀 아닌거 같고 좀더 나은 방법을 뒤져봐야겠다.
이하는 main 을 담고있는 코드.
한줄을 받아서 한줄을 내놓는 최소한의 로직만 담고있는 클래스 echo 를 정의하고 이놈으로 console<echo> 인스턴스를 만들어 io_service 에 붙였다. 당연히 그냥 asio 공부하려고 만들어본 코드니 제구실하기엔 부족한 코드.
아래는 console, session 을 담고있는 코드main.cpp..
#include "console.hpp"
// 걍 간단한 예제니까 operator() 하나만 요구하도록 했다.
// 좀더 써먹으려면 프롬프트제어나.. 등등 많은 기능이 이쪽으로 들어와야겠지?
struct echo
{
string operator()(const string& input)
{
// 간단히 만들다 보니 그만 끝내라는 제어를 예외로 하게됐다.
if(input == "quit") throw runtime_error("damn shit");
// 여기서 적절한 처리를 하면 되겠지.
return input;
}
};
int main()
{
try
{
asio::io_service io_service;
console<echo> con(io_service, tcp::endpoint(tcp::v4(), 4321), "sucks> ");
io_service.run();
}
catch(exception& e)
{
cerr << e.what() << endl;
}
}
console<T> 는 그냥 acceptor 라고 보면 된다. 특정 포트로 tcp 받아서 session<T> 를 까주는 놈. session<T> 는.. 결국 session<echo> 로 인스턴스가 만들어지는데 echo 가 가진 함수를 부르기 위한 소켓관련 작업들을 해주는 놈이다.
이건 include 모음집. 네임스페이스 처리가 짱나서 하나에 몰아두고 쓰는게 좋지.console.hpp..
// 몇줄 안되는 코드지만 async_read_until 때문에 고생했다. 우선 이놈은
// 버퍼로 받는 타입이 다른 어싱크 함수들과는 다르더라. basic_streambuf
// 라.. 문서상에는 std::streambuf 확장이라고 되어있던데.. 헌데 난 C++
// 을 쓰더라도 가능하면 stream 라이브러리쪽을 쓰지 않는 주의(C 쪽
// FILE* 또는 win32 api 선호)라 streambuf 를 다뤄보는건 처음이었다.
//
// 결론적으로 streambuf 는 잘 모른다는 소리. 예제에서는 streambuf 에서
// istream 만들어서 이 스트림을 가지고 버퍼를 읽던데.. 이 무슨
// 삽질인가? 버퍼면 그냥 꺼낼수 있어야지. 그런데 적절히 둘러봐도 멋지게
// 꺼낼 메서드를 못찾아서(몰라서) 결국 한글자씩 뽑아내는 무식한짓을
// 했다... 헐....
//
// 여유나면 C++ stream 쪽도 공부를 좀 해두자.
//
#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#include "boostasio.hpp"
template<class T>
class session
{
public:
// 최초에 프롬프트 찍어주고
session(socket_ptr sock, const string& pr)
: sock_(sock),
prompt_(pr)
{
prompt();
}
private:
socket_ptr sock_;
T proc_;
asio::streambuf buf_;
string prompt_;
// 프롬프트 날려준다.
void prompt()
{
asio::async_write(*sock_,
asio::buffer(prompt_),
bind(&session<T>::handle_prompt,
this,
asio::placeholders::error));
}
// 프롬프트 날아갔으면 한줄 읽을 차례
void handle_prompt(const error_code& error)
{
if(error) return die(error);
readline();
}
// 한줄 읽는다.
void readline()
{
asio::async_read_until(*sock_,
buf_,
"\r\n",
bind(&session<T>::handle_readline,
this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
// 한줄 읽었으면 처리할 차례. 단 빈 문자열이면 프롬프트나 날린다.
void handle_readline(const error_code& error, size_t readed)
{
if(error) return die(error);
// 아래 istream 이 문서상에 나온 사용법. 그런데 이런식의
// 사용법은 좀 거시기하네.
// istream i(&buf_);
// string line;
// getline(i, line);
// 으음. streambuf 로부터 직접 값을 꺼냈다. 이런식의 접근도
// 정상은 아닌거 같은데.. 어쨌거나 마지막에 \r\n 이 들어있으니
// 적절히 처리를 해줘야 했는데 코드를 깔끔하게 하려고 모두
// 읽은후 트리밍해줬다. 릴리즈할 코드였다면 readed-2 만큼
// 읽었겠지. 아니 애초에 한글자씩 꺼내는 이짓을 하지 않고 다른
// 방법을 찾았겠지.
string line;
for(int i = 0; i < readed; ++i)
line.push_back(buf_.sbumpc());
boost::trim(line);
// 빈입력이 들어오면 바로 다시 프롬프트찍도록 했다. 이거 빠지면
// 엔터 마구칠때 보기 흉해진다.
if(line.empty()) return prompt();
proc(line);
}
// 처리.. 처리하는 코드를 템플릿 인자로 받도록 했다. 별의미는 없고 그냥..
void proc(const string& input)
{
try
{
string output(proc_(input));
writeline(output+"\r\n");
}
catch(exception& e)
{
die(e);
}
}
// 처리결과 쏴주고
void writeline(const string& output)
{
asio::async_write(*sock_,
asio::buffer(output),
bind(&session<T>::handle_writeline,
this,
asio::placeholders::error));
}
// 다시 프롬프트 쏠 차례
void handle_writeline(const error_code& error)
{
if(error) return die(error);
prompt();
}
// 에러나면 걍 delete this
void die(const error_code& error)
{
cerr << error.message() << endl;
delete this;
}
void die(exception& e)
{
cerr << e.what() << endl;
delete this;
}
};
// 템플릿 인자로 받은건 괜히 쑈한것. 그나저나 이 T 로 인스턴스를 직접
// 만드는게 아니고 session 에 전달만 하네.. 이거 좀 모양이 별로군.
template <class T>
class console
{
public:
console(asio::io_service& io_service, const tcp::endpoint& endpoint, const string& prompt)
: acceptor_(io_service, endpoint),
prompt_(prompt)
{
start_accept();
}
private:
tcp::acceptor acceptor_;
string prompt_;
void start_accept()
{
socket_ptr childsocket(new tcp::socket(acceptor_.io_service()));
acceptor_.async_accept(*childsocket,
bind(&console<T>::handle_accept,
this,
childsocket,
asio::placeholders::error));
}
void handle_accept(socket_ptr childsocket, const error_code& error)
{
if(error)
{
cerr << error.message() << endl;
return;
}
new session<T>(childsocket, prompt_);
start_accept();
}
};
#endif
음.boostasio.hpp..
#ifndef BOOSTASIO_HPP
#define BOOSTASIO_HPP
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
using namespace std;
namespace asio = boost::asio;
using boost::asio::ip::tcp;
using boost::lexical_cast;
using boost::array;
using boost::bind;
using boost::shared_ptr;
using boost::system::error_code;
typedef shared_ptr<tcp::socket> socket_ptr;
#endif
좀더 뒤져서 asio::streambuf 에서 값을 꺼내는 방법을 하나 더 찾았다.
asio::const_buffer data = buf_.data();
const char* b = asio::buffer_cast<const char*>(data);
const char* e = b + readed;
뭐 대략 이런식인데 이것도 통쾌한 방법은 아닌거 같다. 문서상 그리고 메일링에서도 istream 을 통해 꺼내오는것을 권장하는거 같은데.. iostream 쪽 코드는 영 손대기 그렇구만..
그리고 이런식으로 뽑아쓸경우 consume 으로 버퍼 비워주는것도 잊지말자.
추가.
ReadHandler 같은경우 error 와 readed 를 인자로 받는데 나는 error 나면 readed 무시하고 바로 에러를 내도록 했지만 경우에 따라 readed 를 먼저 처리하고 error 확인해야 할때도 있을거 같은데.. 이건 나중에 asio 를 정말 쓰게되면 다시 만날 문제겠지.
2009년 4월 9일 목요일
boost::asio 를 이용한 tcp 연결 테스터 -- boost::bind 와 boost::function 을 이용해서 두 클래스의 의존성 줄이기
음 제목짓기가 애매하네.
어떤 서버가 tcp 연결을 몇개까지 받는지 테스트해야 했는데 어쩌다 보니 C++ 로 짜게 됐다.(ghc 의 소켓제한 때문에..)
워낙 간단한 코드라 평소 안하던짓을 한번 해보고 코드를 적어둔다.
소켓을 다룰때 많은 세션을 다루려면 이 세션을 다루는 매니저 클래스가 있을법 한데 보통 이경우 세션 클래스가 매니저 클래스를 알아야만 한다(커플링). 이걸 bind 와 function 으로 좀 오버해서 만들어봤다.
물론 이외에도 방법은 많고.. 실무에 써야 한다면 아마도 당연히 그냥 의존성을 그냥 두고 가거나 아니면 signal 류 라이브러리를 썼겠지. 아래 코드는 재미삼아 만들어본것에 불과하다.
어떤 서버가 tcp 연결을 몇개까지 받는지 테스트해야 했는데 어쩌다 보니 C++ 로 짜게 됐다.(ghc 의 소켓제한 때문에..)
워낙 간단한 코드라 평소 안하던짓을 한번 해보고 코드를 적어둔다.
소켓을 다룰때 많은 세션을 다루려면 이 세션을 다루는 매니저 클래스가 있을법 한데 보통 이경우 세션 클래스가 매니저 클래스를 알아야만 한다(커플링). 이걸 bind 와 function 으로 좀 오버해서 만들어봤다.
물론 이외에도 방법은 많고.. 실무에 써야 한다면 아마도 당연히 그냥 의존성을 그냥 두고 가거나 아니면 signal 류 라이브러리를 썼겠지. 아래 코드는 재미삼아 만들어본것에 불과하다.
펼쳐두기..
// 테스트용도로 프로그램을 작성해야 했는데 boost 의 asio, function,
// bind 를 함 써봤다. asio 는 뭐 워낙 문서 잘되있고 쓰기 쉬운놈이라
// 그냥 문서 따라하면 되고..
//
// 잠깐 재미삼아 드러운 코드를 만들어봤는데 대강 적어둔다. killer 란
// 이름은 좀 장난스러운 건데 manager 정도로 지을걸 그랬구먼.
//
// killer 는 일종의 매니저 클래스로 필요에 따라 session 을 생성하면서
// sessions 타입으로 적절히 관리(걍 들고만 있는다)를 하며 session
// 클래스는 소켓이 끊어지면 killer 의 어떤 메서드를 불러서 자신이
// 종료된다는것을 알리고 delete this 로 죽는다.
//
// 평소대로라면 session 에 killer 의 레퍼런스를 넘겨서 들고있다가
// 필요할때 이 레퍼런스를 통해서 killer 의 멤버를 불렀겠지...
//
// 그런데 이번에는 그냥 재미삼아서 session 클래스와 killer 클래스의
// 커플링을 제거해보려고 했다..
//
// OOP 식으로 접근하자면 매니저클래스의 추상클래스를 하나 만들어서
// killer 가 이를 상속받고 session 에는 추상클래스 포인터를 넘겨서
// session 가 구체클래스인 killer 가 아니라 추상클래스를 보도록
// 하겠지...
//
// 그건 별 재미가 없고 이번에는 killer 가 session 이 부를 자신의
// 메서드를 하나의 함수객체로 포장해서 session 에 넘겨버리고 session 은
// 이놈만 부르면 되도록 해봤다. killer 입장에서는 session 을 만들때
// session 이 상황에 따라 부를 함수들을 지정할수 있는 장점이 있고
// session 입장에서는 함수객체 라는 아주 추상화된 타입만 바라보면 되니
// 쓸데없이 클래스 끼고 상속받는것보다는 좀더 깔끔하다 하겠다.
//
// 1. killer::insert_session 에서 새로운 session 인스턴스를 만들어야
// 겠지. 이때 생성자 안에 필요한 모든 정보를 때려박으려
// 했었다. 그런데 이 정보중에는 session 인스턴스의 포인터가
// 포함되어있고, 이는 생성자가 끝나야만 유효한 값이니 결국 생성자
// 말고 또 다른 함수 session::set_finalizer 를 만들수밖에 없었다.
//
// 2. 필요한 작업은 하나인데 함수가 둘(new, set_finalizer) 로
// 쪼개졌으니 이걸 하나로 묶어야 했다. session 의 생성자를 private
// 로 감춰버리고 make 라는 스태틱 멤버를 추가해서 이놈이
// 생성자역할을 대신 하도록 했다. 물론 set_finalizer 로 받을
// 함수객체도 이놈이 받아야했다.
//
// 3. 이거 좀 억지스러운 코드지만.. session::make 에
// killer::remove_session 를 넘기기 위해 바인딩을 할때 마찬가지로
// session 의 포인터가 필요한 문제는 여전히 남아있다. 다행이 잘난
// boost::bind 가 콤포징을 지원하기 때문에 간단히 해결. _1 를 일단
// 바인딩 해서 넘기고 make 안에서 포인터를 마저 바인딩 하도록
// 했다. 당연히 이런 식의 코드는 공유코드를 만들때는 주의하도록
// 해야겠지.
//
// 4. session::make 에서는 먼저 session 을 생성하고 그 포인터에 인자로
// 받은 반쪽함수를 콤포징 해서 하나의 완전한 함수객체를 만들어
// 기억해두고 소멸될때 부르도록 했다.
//
// 중요.
//
// session 을 shared_ptr 로 들고다니지 않은 이유는 session 을 내가
// 강제적으로 delete 하는 구조라 그런것. 이런식의 구조라면
// shared_ptr 가 객체가 이미 delete 된걸 모르게 되니 당연히 죽는다.
//
// delete this 를 버리고 소멸을 shared_ptr 에 의존하는 식으로
// 수정하거나 weak_ptr 을 쓰면 해결이 가능하다.
//
// 물론.. 위에서 언급한 모든 작업들은 그다지 복잡한 작업이 아니므로
// 이렇게 boost 질을 한다는것 자체가 오버헤드. asio 는 libevent 를
// 쓰는것에 비해 월등한 장점이 있으니 잘 활용을 해야겠지만
// bind,function 등의 과도한 사용은 피하도록 하자.
#include <iostream>
#include <string>
#include <list>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/function.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
using namespace std;
namespace asio = boost::asio;
namespace posix_time = boost::posix_time;
using boost::array;
using boost::asio::ip::tcp;
using boost::bind;
using boost::function;
using boost::lexical_cast;
using boost::shared_ptr;
using boost::system::error_code;
typedef shared_ptr<tcp::socket> socket_ptr;
// 연결된 소켓을 받아서 그냥 들고만 있는다. 좀더 정확히 말하면 소켓
// 끊어지는것을 감시해야 하니 async_read_some 을 걸어두고 읽히는
// 데이타는 그냥 무시한다. 만약 소켓이 끊어지면 killer 의 특정 메서드를
// 불러야 하는데 이때 killer 와 의존성을 완전히 없애기 위해서 function
// 을 써봤다.
class session
{
public:
static session* make(socket_ptr& sock, function<void(session*)> fun)
{
session* s = new session(sock);
s->set_finalizer(bind(fun,s));
return s;
}
~session()
{
finalizer_();
}
private:
function<void()> finalizer_;
socket_ptr sock_;
array<char, 8> buf_;
session(socket_ptr& sock) : sock_(sock)
{
readmore();
}
void set_finalizer(const function<void()>& fun)
{
// 이걸 원래 생성자에 넣었어야 모양이 더 좋은데 생성자가 끝나야
// 이놈의 포인터가 떨어지기 때문에 포인터까지 포함해서 바인딩을
// 할수가 없었다. 그렇다고 function<void(session)> 타입을
// 받아서 후에 finalizer_(this)) 뭐 이딴식으로 부르는건 별
// 재미없는 코드라... 별수없이 생성자와는 별도로 함수가
// 추가되었다.
//
// 물론 생성자와 set_finalizer 는 항상 붙어다녀야 하는 놈이므로
// 생성자에 해당하는 함수 make 를 만들어주고 생성자는 private
// 으로 빼돌렸다.
finalizer_ = fun;
}
void readmore()
{
sock_->async_read_some(asio::buffer(buf_),
bind(&session::handle_read_some,
this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void handle_read_some(const error_code& error, size_t readed)
{
if(error)
{
// 소켓이 끊어지면 delete this
cerr << "handle_read_some: " << error.message() << endl;
delete this;
return;
}
cout << "(unexpected) incoming data" << endl;
readmore();
}
};
// `host:port` 에 `interval` 간격으로 tcp 연결을 최대 `count` 개까지
// 하는놈
class killer
{
typedef list<session*> sessions;
public:
killer(asio::io_service& io_service,
const string& host,
int port,
int count,
int interval) : timer_(io_service),
target_(asio::ip::address::from_string(host), port),
count_(count),
interval_(interval)
{
set_timer();
}
private:
asio::deadline_timer timer_;
tcp::endpoint target_;
int count_;
int interval_;
sessions sessions_;
// 타이머 세팅
void set_timer()
{
timer_.expires_from_now(posix_time::milliseconds(interval_));
timer_.async_wait(bind(&killer::handle_timer, this, asio::placeholders::error));
}
// 타이머핸들러. 새 연결을 시도한다.
void handle_timer(const error_code& error)
{
if(error)
{
cerr << "handle_timer: " << error.message() << endl;
return;
}
socket_ptr sock(new tcp::socket(timer_.io_service()));
sock->async_connect(target_,
bind(&killer::handle_connect,
this,
sock,
asio::placeholders::error));
}
// 컨넥션 핸들러. 떨어진 소켓으로 새 세션을 만들고 필요하다면 다시 타이머를 세팅한다
void handle_connect(socket_ptr& sock, const error_code& error)
{
if(error)
{
cerr << "handle_connect: " << error.message() << endl;
return;
}
insert_session(sock);
if(sessions_.size() < count_)
set_timer();
}
// 소켓으로 새 세션을 만들어 기억해둔다.
void insert_session(socket_ptr& sock)
{
session* ses = session::make(sock,
bind(&killer::remove_session,
this,
_1));
sessions_.push_back(ses);
dump_session_counts();
}
// 세션을 삭제한다.
void remove_session(session* ses)
{
sessions_.remove(ses);
dump_session_counts();
}
// `현재세션`/`최대세션` 출력
void dump_session_counts()
{
cout << "sessions: " << sessions_.size() << "/" << count_ << endl;
}
};
int main(int argc, char* argv[])
{
try
{
if(argc != 5)
{
cout << "Usage: foo <host> <port> <count> <interval>" << endl;
return 1;
}
string host(argv[1]);
int port(lexical_cast<int>(argv[2]));
int count(lexical_cast<int>(argv[3]));
int interval(lexical_cast<int>(argv[4]));
asio::io_service io_service;
killer k(io_service, host, port, count, interval);
io_service.run();
}
catch(exception& e)
{
cerr << e.what() << endl;
}
}
추가.
어쩌다보니 연결 받는놈도 필요해서 만들었다. 걍 같이 적어둔다.펼쳐두기..
// tcpkiller 로 찔러볼수있는 놈. 즉 여러개의 소켓을 accept 만 해보는
// 간단한 프로그램. java 로 만들어진 서버가 컨넥션을 몇개 못받길래 OS
// 환경상의 문제가 아닌가 해서 만들어본것.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
using namespace std;
namespace asio = boost::asio;
using boost::asio::ip::tcp;
using boost::lexical_cast;
using boost::array;
using boost::bind;
using boost::shared_ptr;
using boost::system::error_code;
typedef shared_ptr<tcp::socket> socket_ptr;
// idle 세션이 몇개나 떴는지는 다른 클래스에 일일히 보고하지 않고 그냥
// 스태틱 멤버를 둬서 생성 소멸시 카운팅 하도록 했다. asio 에 thread 를
// 하나만 썼기때문에 따로 락을 걸지는 않았다.
class idle
{
public:
idle(socket_ptr sock) : sock_(sock)
{
++instances_;
dump_idle_counts();
readmore();
}
~idle()
{
--instances_;
dump_idle_counts();
}
private:
socket_ptr sock_;
array<char, 8> buf_;
static size_t instances_;
void readmore()
{
sock_->async_read_some(asio::buffer(buf_),
bind(&idle::handle_read_some,
this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void handle_read_some(const error_code& error, size_t readed)
{
if(error)
{
cerr << error.message() << endl;
delete this;
return;
}
readmore();
}
void dump_idle_counts()
{
cout << "idle: " << instances_ << endl;
}
};
size_t idle::instances_ = 0;
template<class T>
class async_acceptorT
{
public:
async_acceptorT(asio::io_service& io_service, const tcp::endpoint& endpoint)
: acceptor_(io_service, endpoint)
{
start_accept();
}
private:
tcp::acceptor acceptor_;
void start_accept()
{
socket_ptr childsocket(new tcp::socket(acceptor_.io_service()));
acceptor_.async_accept(*childsocket,
bind(&async_acceptorT::handle_accept,
this,
childsocket,
asio::placeholders::error));
}
void handle_accept(socket_ptr childsocket, const error_code& error)
{
if(error)
{
cerr << error.message() << endl;
return;
}
new T(childsocket);
start_accept();
}
};
int main(int argc, char* argv[])
{
try
{
int port = argc == 2 ? lexical_cast<int>(argv[1]) : 9413;
cout << "listen: " << port << endl;
asio::io_service io_service;
async_acceptorT<idle> target(io_service, tcp::endpoint(tcp::v4(), port));
io_service.run();
}
catch(exception& e)
{
cerr << e.what() << endl;
}
}
피드 구독하기:
글 (Atom)