標籤:else stream 資料 asi creat 拷貝構造 ptime 管理 track
同步服務端同步服務端也相當簡單。它須要兩個線程,一個負責接收新的client。另外一個負責處理已經存在的client。
它不能使用單線程;等帶一個新的client是一個堵塞操作,所以我們須要另外一個線程來處理已經存在的client。正常來說服務端都比client要難實現。一方面,它要管理全部已經串連的client。由於我們是同步的,所以我們須要至少兩個線程。一個接受新的client串連(由於accept()是堵塞的)而還有一個負責回複已經存在的client。
void accept_thread() { ip::tcp::acceptor acceptor(service,
ip::tcp::endpoint(ip::tcp::v4(),
8001)); while ( true) {
client_ptr new_( new talk_to_client); acceptor.accept(new_->sock()); boost::recursive_mutex::scoped_lock lk(cs); clients.push_back(new_);
} }
void handle_clients_thread() { while ( true) {
boost::this_thread::sleep( millisec(1)); boost::recursive_mutex::scoped_lock lk(cs); for(array::iterator b = clients.begin(),e = clients.end(); b
!= e; ++b)
(*b)->answer_to_client(); // 刪除已經逾時的用戶端 clients.erase(std::remove_if(clients.begin(), clients.end(),
boost::bind(&talk_to_client::timed_out,_1)),
clients.end()); }
} int main(int argc, char* argv[]) {
boost::thread_group threads; threads.create_thread(accept_thread); threads.create_thread(handle_clients_thread); threads.join_all();
}
為了分別處理client發送過來的請求我們須要儲存一個client的列表。每一個talk_to_client執行個體都擁有一個socket,socket類是不支援拷貝構造的,所以假設你想要把它們儲存在一個std::vector方法中,你須要一個指向它的智能指標。這裡有兩種實現的方式:在talk_to_client內部儲存一個指向socket的智能指標然後建立一個talk_to_client執行個體的數組,或者讓talk_to_client執行個體用變數的方式儲存socket,然後建立一個指向talk_to_client智能指標的數組。我選擇後者。可是你也能夠選前面的方式:
typedef boost::shared_ptr<talk_to_client> client_ptr; typedef std::vector<client_ptr> array; array clients; boost::recursive_mutex cs; // 用安全執行緒的方式訪問用戶端數組
talk_to_client的主要代碼例如以下:
struct talk_to_client : boost::enable_shared_from_this<talk_to_client> {
talk_to_client() { ... } std::string username() const { return username_; } void answer_to_client() {
try { read_request();
process_request(); } catch ( boost::system::system_error&) {
stop(); }
if ( timed_out()) stop();
} void set_clients_changed() { clients_changed_ = true; } ip::tcp::socket & sock() { return sock_; } bool timed_out() const {
ptime now = microsec_clock::local_time(); long long ms = (now - last_ping).total_milliseconds(); return ms > 5000 ;
} void stop() {
boost::system::error_code err; sock_.close(err); }
void read_request() {
if ( sock_.available())
read_)); }
... private:
already_read_ += sock_.read_some( buffer(buff_ + already_read_, max_msg - already_
// ... same as in Synchronous Client bool clients_changed_; ptime last_ping;
};
上述代碼擁有很好的自釋。最重要的方法是read_request()。
它僅僅有在存在有效資料的情況才讀取,這種話,服務端永遠不會堵塞:
void process_request() { bool found_enter = std::find(buff_, buff_ + already_read_, ‘\n‘)
< buff_ + already_read_;
if ( !found_enter) return; // message is not full
// process the msg last_ping = microsec_clock::local_time(); size_t pos = std::find(buff_, buff_ + already_read_, ‘\n‘) -
buff_; std::string msg(buff_, pos); std::copy(buff_ + already_read_, buff_ + max_msg, buff_); already_read_ -= pos + 1;
if ( msg.find("login ") == 0) on_login(msg); else if ( msg.find("ping") == 0) on_ping(); else if ( msg.find("ask_clients") == 0) on_clients(); else std::cerr << "invalid msg " << msg << std::endl;
} void on_login(const std::string & msg) {
std::istringstream in(msg); in >> username_ >> username_; write("login ok\n"); update_clients_changed();
}
void on_ping() {
write(clients_changed_ ? "ping client_list_changed\n" : "ping
ok\n"); clients_changed_ = false;
} void on_clients() {
std::string msg; { boost::recursive_mutex::scoped_lock lk(cs);
for( array::const_iterator b = clients.begin(), e = clients. end() ;
b != e; ++b) msg += (*b)->username() + " ";
}
write("clients " + msg + "\n"); }
void write(const std::string & msg) { sock_.write_some(buffer(msg)); }觀察process_request()。當我們讀取到足夠多有效資料時,我們須要知道我們是否已經讀取到整個訊息(假設found_enter為真)。這樣做的話。我們能夠使我們避免一次讀多個訊息的可能(’\n’之後的訊息被儲存到緩衝區中)。然後我們解析讀取到的整個訊息。剩下的代碼都是易懂的。
Boost.Asio c++ 網路編程翻譯(18)