2011-08-12 14 views
6

मैं वर्तमान में एक बहुत ही सरल बढ़ावा :: asio सर्वर है कि जोड़ने (गूगल आद्य बफ़र्स का उपयोग) पर एक स्थिति अद्यतन भेजता है (गूगल आद्य बफ़र्स के साथ):बढ़ावा asio टीसीपी सत्र खुला रखने

try 
{ 
    boost::asio::io_service io_service; 
    tcp::acceptor acceptor(io_service,tcp::endpoint(tcp::v4(), 13)); 
    for (;;) 
    { 
    tcp::socket socket(io_service); 
    acceptor.accept(socket); 
    ... 
    std::stringstream message; 
    protoMsg.SerializeToOstream(&message); 
    boost::system::error_code ignored_error; 
    boost::asio::write(socket, boost::asio::buffer(message.str()), ignored_error); 
    } 
} 
catch (std::exception& e) { } 

मैं इसे एक नया कनेक्शन स्वीकार करने के बाद पहले पढ़ने के लिए विस्तारित करना चाहता हूं, जांचें कि कौन सा अनुरोध प्राप्त हुआ था, और इस संदेश के आधार पर अलग-अलग संदेश वापस भेज दें। मैं टीसीपी कनेक्शन को भी खोलना चाहता हूं ताकि क्लाइंट को फिर से कनेक्ट करने की आवश्यकता न हो, और कई क्लाइंट्स को संभालना चाहें (कई नहीं, शायद 2 या 3)।

मैंने बूस्ट एएसओ, अर्थात् the async time tcp server और chat server पर कुछ उदाहरणों पर एक नज़र डाली, लेकिन दोनों मेरे सिर tbh पर थोड़ा सा हैं। मैं यह भी समझ में नहीं आता कि मुझे एसिंक सर्वर की आवश्यकता है या नहीं। मुझे लगता है कि मैं सिर्फ acceptor.accept(socket) के बाद पढ़ सकता हूं, लेकिन मुझे लगता है कि मैं आगे के अनुरोधों को सुनना जारी रखूंगा। और अगर मैं एक लूप में जाता हूं तो मुझे लगता है कि इसका मतलब यह होगा कि मैं केवल एक ग्राहक को संभाल सकता हूं। तो मुझे लगता है कि इसका मतलब है कि मुझे एसिंक जाना है? क्या कोई आसान उदाहरण हो सकता है कि कोड की 250 लाइनें न हों? या क्या मुझे बस उन उदाहरणों के माध्यम से अपना रास्ता काटना है? धन्यवाद

उत्तर

12

Boost.Asio दस्तावेज से आपके द्वारा उल्लेख किए जाने वाले उदाहरण वास्तव में यह देखने के लिए बहुत अच्छे हैं कि चीजें कैसे काम करती हैं। आप सही हैं कि पहले इसे समझना थोड़ा मुश्किल लग सकता है, खासकर यदि आप इन अवधारणाओं के लिए नए हैं। हालांकि, मैं अनुशंसा करता हूं कि आप चैट सर्वर उदाहरण से शुरू करें और इसे अपनी मशीन पर बनाया जाए। इससे आपको यह जानने के लिए चीजों को देखने और चीजों को बदलने शुरू करने की अनुमति मिल जाएगी। मुझे कुछ चीजों के माध्यम से आपको मार्गदर्शन करने दें जो मुझे शुरू करने के लिए महत्वपूर्ण लगता है।

अपने विवरण से आप जो करना चाहते हैं, ऐसा लगता है कि चैट सर्वर आपको एक अच्छा प्रारंभिक बिंदु देता है क्योंकि आपके पास पहले से ही समान टुकड़े हैं। सर्वर एसिंक्रोनस होने के नाते आप जो चाहते हैं वह एक ही थ्रेड के साथ कई क्लाइंट को आसानी से संभाल सकता है। शुरुआत से कुछ भी जटिल नहीं है।

सरलीकृत, इस मामले में एसिंक्रोनस का मतलब है कि आपका सर्वर एक कतार बंद कर देता है, एक हैंडलर (कार्य) लेता है और इसे निष्पादित करता है। अगर कतार पर कुछ भी नहीं है, तो यह कतार पर कुछ लगाने के लिए इंतजार कर रहा है। आपके मामले में इसका मतलब है कि यह किसी ग्राहक से कनेक्ट हो सकता है, किसी ग्राहक से संदेश का एक नया पठन या ऐसा कुछ। इसके लिए काम करने के लिए, प्रत्येक हैंडलर (किसी विशेष घटना पर प्रतिक्रिया को संभालने वाला फ़ंक्शन) स्थापित करने की आवश्यकता होती है।

मुझे चैट सर्वर उदाहरण से कोड का उपयोग करके थोड़ा सा समझाएं।

server source file में, आप chat_server कक्षा देखें जो कन्स्ट्रक्टर में start_accept पर कॉल करता है। यहां स्वीकार हैंडलर स्थापित हो जाता है।

void start_accept() 
{ 
    chat_session_ptr new_session(new chat_session(io_service_, room_)); // 1 
    acceptor_.async_accept(new_session->socket(),      // 2 
     boost::bind(&chat_server::handle_accept, this, new_session,  // 3 
      boost::asio::placeholders::error));       // 4 
} 

पंक्ति 1: एक chat_session वस्तु जो एक ग्राहक और सर्वर के बीच एक सत्र का प्रतिनिधित्व करता बनाई गई है। स्वीकृति के लिए एक सत्र बनाया गया है (कोई क्लाइंट अभी तक कनेक्ट नहीं हुआ है)।

पंक्ति 2: एक अतुल्यकालिक सॉकेट के लिए स्वीकार करते हैं ...

लाइन 3: ... chat_server::handle_accept कॉल करने के लिए जब ऐसा होता है बाध्य। सत्र को पहले क्लाइंट द्वारा उपयोग किए जाने के साथ पास किया जाता है जो कनेक्ट करता है।

अब, अगर हम handle_accept देखते हैं तो हम देखते हैं कि क्लाइंट कनेक्ट पर, start सत्र के लिए कहा जाता है (यह केवल सर्वर और इस क्लाइंट के बीच सामान शुरू करता है)।आखिर में अन्य ग्राहक भी कनेक्ट करना चाहते हैं, तो एक नई स्वीकृति बकाया है।

void handle_accept(chat_session_ptr session, 
        const boost::system::error_code& error) 
{ 
    if (!error) 
    { 
     session->start(); 
    } 
    start_accept(); 
} 

यही वह है जो आप चाहते हैं। आने वाले कनेक्शन के लिए एक उत्कृष्ट स्वीकृति। और यदि एकाधिक ग्राहक कनेक्ट हो सकते हैं, तो हमेशा इन बकाया में से एक होना चाहिए ताकि सर्वर स्वीकृति को संभाल सके।

सर्वर और क्लाइंट इंटरैक्ट कैसे सत्र में हैं और आप उसी डिजाइन का पालन कर सकते हैं और जो भी आप चाहते हैं उसे संशोधित कर सकते हैं। आप उल्लेख करते हैं कि सर्वर को क्या भेजा जाता है और विभिन्न चीजें करने की आवश्यकता होती है। chat_session और start फ़ंक्शन पर एक नज़र डालें जिसे सर्वर द्वारा handle_accept में बुलाया गया था।

void start() 
{ 
    room_.join(shared_from_this()); 
    boost::asio::async_read(socket_, 
     boost::asio::buffer(read_msg_.data(), chat_message::header_length), 
     boost::bind(
      &chat_session::handle_read_header, shared_from_this(), 
      boost::asio::placeholders::error)); 
} 

boost::asio::async_read पर कॉल करना महत्वपूर्ण है। यह वही है जो आप चाहते हैं। यह सॉकेट पर एक उत्कृष्ट पढ़ने देता है, इसलिए सर्वर क्लाइंट भेजता है जो पढ़ सकता है। एक हैंडलर (फ़ंक्शन) है जो इस घटना chat_session::handle_read_header से जुड़ा हुआ है। जब भी सर्वर सॉकेट पर कुछ पढ़ता है तो इसे कॉल किया जाएगा। इस हैंडलर फ़ंक्शन में आप यह निर्धारित करने के लिए अपना विशिष्ट कोड डालना शुरू कर सकते हैं कि एक विशिष्ट संदेश भेजा गया है और इसी तरह क्या करना है।

यह जानना महत्वपूर्ण है कि जब भी इन एसिंक्रोनस बूस्ट को कॉल किया जाता है :: एएसओ फ़ंक्शन चीजें उस कॉल के भीतर नहीं होतीं (यानी यदि आप फ़ंक्शन को पढ़ते हैं तो सॉकेट पढ़ा नहीं जाता है)। यह असीमित पहलू है। आप बस कुछ के लिए हैंडलर पंजीकृत करते हैं और ऐसा होने पर आपके कोड को वापस बुलाया जाता है। इसलिए, जब इसे पढ़ा जाता है तो यह तुरंत वापस आ जाएगा और आप सर्वर के लिए handle_accept में वापस आ गए हैं (यदि आप अनुसरण करते हैं कि चीजें कैसे कॉल की जाती हैं)। और यदि आपको याद है तो हम एक और एसिंक्रोनस स्वीकृति सेट अप करने के लिए start_accept पर भी कॉल करते हैं। इस बिंदु पर आपके पास दो उत्कृष्ट हैंडलर हैं जो किसी अन्य क्लाइंट को कनेक्ट करने या पहले क्लाइंट को कुछ भेजने के लिए इंतजार कर रहे हैं। पहले क्या होता है इसके आधार पर, उस विशिष्ट हैंडलर को बुलाया जाएगा।

यह भी समझना महत्वपूर्ण है कि जब भी कुछ चल रहा है, तब तक यह निर्बाध हो जाएगा जब तक कि उसे करने की आवश्यकता नहीं होती है। अन्य हैंडलर को तब भी इंतजार करना पड़ता है जब बकाया घटनाएं होती हैं जो उन्हें ट्रिगर करती हैं।

अंत में, सर्वर चलाने के लिए आपको io_service की आवश्यकता होगी जो असियो में एक केंद्रीय अवधारणा है।

io_service.run(); 

यह एक लाइन आप main समारोह में देखते हैं। यह सिर्फ इतना कहता है कि थ्रेड (उदाहरण में केवल एक) को io_service चलाया जाना चाहिए, जो कि कतार है जहां काम करने के लिए हैंडलर लगाए जाते हैं। कुछ भी नहीं होने पर, io_service बस प्रतीक्षा करता है (निश्चित रूप से मुख्य धागे को अवरुद्ध करता है)।

मुझे उम्मीद है कि यह आपको जो करना है, उसके साथ शुरू करने में आपकी सहायता करता है। ऐसी कई चीजें हैं जो आप कर सकते हैं और सीखने के लिए चीजें हैं। मुझे यह सॉफ्टवेयर का एक बड़ा टुकड़ा मिल गया है! सौभाग्य!

+0

चीयर्स धन्यवाद। आप सही हैं, मुझे वास्तव में उनके माध्यम से काटने की ज़रूरत है। एक बात जो मैं समझ नहीं पा रहा हूं वह उदाहरणों में साझा पॉइंटर्स का मजबूत उपयोग है। वे कहाँ रखा जाता है? जादूगर के रूप में जादूगर के अंदर? यदि कनेक्शन मर जाता है, तो क्या वे स्वचालित रूप से साफ हो जाएंगे? धन्यवाद – Cookie

+0

साझा पॉइंटर्स का उपयोग कुछ वस्तुओं को हैंडलर के अंदर "अंदर" रखने के लिए है। असियो में केवल हैंडलर है (फ़ंक्शन :: बाइंड से प्राप्त हुआ है) और साझा पॉइंटर वहां बाध्य है। इस तरह साझा पॉइंटर में ऑब्जेक्ट तब तक जीवित रखा जाता है जब तक कि हैंडलर जीवित रहता है, जो महत्वपूर्ण है। सत्र सत्र में साझा_फ्रॉम_थिस() के रूप में सत्र को सभी बूस्ट :: बाइंड के रूप में पारित किया जाता है, जो आप देखते हैं, जो कि जीवन भर के रूप में काफी अच्छा है, उसके बाद हर समय एसिंक्रोनस हैंडलर का स्वामित्व होता है, जो हाँ के मामले में सफाई को संभालेगा त्रुटि। – murrekatt

3

किसी और मामले किसी में, यह करने के लिए चाहता है यहाँ कम से कम ऊपर हो रहा है: (ट्यूटोरियल के समान है, लेकिन थोड़ा छोटा और थोड़ी अलग)

class Session : public boost::enable_shared_from_this<Session> 
{ 
    tcp::socket socket; 
    char buf[1000]; 
public: 
    Session(boost::asio::io_service& io_service) 
     : socket(io_service) { } 
    tcp::socket& SocketRef() { return socket; } 
    void Read() { 
     boost::asio::async_read(socket,boost::asio::buffer(buf),boost::asio::transfer_at_least(1),boost::bind(&Session::Handle_Read,shared_from_this(),boost::asio::placeholders::error)); 
    } 
    void Handle_Read(const boost::system::error_code& error) { 
     if (!error) 
     { 
      //read from buffer and handle requests 
      //if you want to write sth, you can do it sync. here: e.g. boost::asio::write(socket, ..., ignored_error); 
      Read(); 
     } 
    } 
}; 

typedef boost::shared_ptr<Session> SessionPtr; 

class Server 
{ 
    boost::asio::io_service io_service; 
    tcp::acceptor acceptor; 
public: 
    Server() : acceptor(io_service,tcp::endpoint(tcp::v4(), 13)) { } 
    ~Server() { } 
    void operator()() { StartAccept(); io_service.run(); } 
    void StartAccept() { 
     SessionPtr session_ptr(new Session(io_service)); 
     acceptor.async_accept(session_ptr->SocketRef(),boost::bind(&Server::HandleAccept,this,session_ptr,boost::asio::placeholders::error)); 
    } 
    void HandleAccept(SessionPtr session,const boost::system::error_code& error) { 
     if (!error) 
      session->Read(); 
     StartAccept(); 
    } 
}; 

मैं क्या परीक्षण के माध्यम से एकत्र से और त्रुटि और पढ़ना: मैं ऑपरेटर()() में इसे लात मारता हूं ताकि आप इसे अतिरिक्त थ्रेड में पृष्ठभूमि में चला सकें। आप एक सर्वर उदाहरण चलाते हैं। एकाधिक ग्राहकों को संभालने के लिए, आपको एक अतिरिक्त कक्षा की आवश्यकता है, मैंने इसे एक सत्र कक्षा कहा है। मृत सत्रों को साफ करने के लिए एएसओ के लिए, आपको ऊपर बताए गए साझा सूचक की आवश्यकता है। अन्यथा कोड आपको शुरू करना चाहिए।

संबंधित मुद्दे