बहुत पहले नहीं,
पायथन में
आईसीक्यू के बारे में एक लेख प्रकाशित किया गया था, जिसने मुझे विषय को विकसित करने के लिए प्रेरित किया, हालांकि थोड़ा अलग दिशा में। कुछ साल पहले मुझे होम इंटरनेट के साथ कठिनाइयाँ थीं: केवल स्थानीय नेटवर्क तक पहुंच, बाहरी दुनिया के साथ संचार से केवल आईसीक्यू और स्थानीय जैबर सर्वर तक; कोई और रास्ता नहीं था। नतीजतन, विचार का जन्म XMPP में HTTP ट्रैफ़िक को टनल करने के लिए हुआ था।
योजना
यह योजना तीन मुख्य घटकों पर आधारित है:
- बॉट सर्वर : HTTP अनुरोधों के साथ संदेश प्राप्त करता है, निष्पादित करता है, एन्कोड करता है और क्लाइंट को परिणाम भेजता है
- बॉट क्लाइंट : HTTP अनुरोधों के बारे में सर्वर जानकारी भेजता है जिसे निष्पादित करने की आवश्यकता होती है, परिणाम की प्रतीक्षा करता है, प्रक्रिया करता है और आगे उपयोग के लिए तैयार अनुरोध के परिणाम को वापस करता है
- HTTP- प्रॉक्सी : एक प्रॉक्सी सर्वर जो एक बॉट क्लाइंट का उपयोग करके HTTP रिक्वेस्ट को प्रोसेस करता है
घटकों को निम्नानुसार व्यवस्थित किया जाता है: इंटरनेट एक्सेस के साथ एक दूरस्थ मशीन पर, एक बॉट सर्वर लॉन्च किया जाता है।
लोकलहोस्ट पर, बॉट क्लाइंट और प्रॉक्सिस लॉन्च किए जाते हैं; क्लाइंट एप्लिकेशन हमारे प्रॉक्सी का उपयोग करने के लिए कॉन्फ़िगर किए गए हैं, उदाहरण के लिए:
$ http_proxy="localhost:3128" wget ...
बॉट-क्लाइंट के साथ बॉट-सर्वर को इंटरैक्ट करने के लिए एक सरल एक्सएमएल-आधारित प्रोटोकॉल का उपयोग किया जाता है।
Example.com इंडेक्स पेज डाउनलोड करने का अनुरोध:
<url>http://example.com</url>
उत्तर है:
<answer chunk="2" count="19"><data>encoded_data</data></answer>
उत्तर में कई भाग हैं, chunk'ov। यहाँ
चंक ,
चंक की संख्या है,
गिनती कुल की संख्या है जिसमें अनुरोध का जवाब विभाजित किया गया था।
एन्कोडेड_डेटा - बेस 64 एनकोडेड रिस्पॉन्स पीस।
अधिक स्पष्टता के लिए, मैं चित्र को चित्रमय रूप से प्रस्तुत करूंगा:
स्थानीय
+ ------------------------------------------------- ---------------------------------- +
| http- क्लाइंट (ब्राउज़र, wget) -> http-प्रॉक्सी -> बॉट-क्लाइंट |
+ ------------------------------------------------- ---------------------------------- +
/ \ _
||
\ /
दूरस्थ
+ ------------------------------------------------- ---------------------------------- +
| बॉट-सर्वर |
+ ------------------------------------------------- ---------------------------------- +
कार्यान्वयन
सामान्य जानकारी
XMPP के साथ काम के लिए
xmpppy का उपयोग किया जाता है। कोई मुश्किल विशेषताओं की आवश्यकता नहीं है, आपको बस आने वाले संदेशों को संसाधित करने और उत्तर भेजने की आवश्यकता है। XML मानक पुस्तकालय का उपयोग करके पार्स और उत्पन्न होता है -
xml.dom.minidom ।
बॉट सर्वर
सर्वर का कार्य डाउनलोड अनुरोध प्राप्त करना है, उन्हें लाइब्रेरी में भेजना है, जो स्वयं यह पता लगाएगा कि क्या डाउनलोड करने और परिणाम को वापस करने की आवश्यकता है, और सर्वर इस परिणाम को क्लाइंट को अग्रेषित करेगा।
एक सरलीकृत योजना में, सर्वर-साइड संदेश प्रसंस्करण इस तरह दिखता है:
import xmpp from Fetcher import Fetcher fetcher = None def message_callback(con, msg): global fetcher if msg.getBody(): try: ret = fetcher.process_command(msg.getBody()) except: ret = ["failed to process command"] for i in ret: reply = xmpp.Message(msg.getFrom(), i) reply.setType('chat') con.send(reply) if __name__ == "__main__": jid = xmpp.JID("my@server.jid") user = jid.getNode() server = jid.getDomain() password = "secret" conn = xmpp.Client(server, debug=[]) conres = conn.connect() authres = conn.auth(user, password, resource="foo") conn.RegisterHandler('message', message_callback) conn.sendInitPresence() fetcher = Fetcher() while True: conn.Process(1)
मैंने जानबूझकर त्रुटि हैंडलिंग और हार्डकोड मानों को हटा दिया ताकि कोड अधिक कॉम्पैक्ट और पढ़ने में आसान हो। तो यहाँ क्या हो रहा है? हम जैबर सर्वर से कनेक्ट करते हैं और संदेश हैंडलर को लटकाते हैं:
conn.RegisterHandler('message', message_callback)
इस प्रकार, प्रत्येक नए आने वाले संदेश के लिए, हमारे
message_callback (con, msg) फ़ंक्शन को कॉल किया जाएगा, जिसके तर्क कनेक्शन हैंडल और संदेश स्वयं होंगे। फ़ंक्शन स्वयं
फ़ॉचर क्लास से कमांड हैंडलर को कॉल करता है, जो सभी "गंदे" काम करता है और क्लाइंट को दिए गए विखंडू की सूची देता है। यह सब है, यह वह जगह है जहां सर्वर समाप्त होता है।
फ़ेचर
Fetcher वर्ग HTTP अनुरोधों को निष्पादित और एन्कोडिंग के बहुत तर्क को लागू करता है। मैंने पूरा कोड नहीं दिया है, इसे लेख से जुड़े संग्रह में देखा जा सकता है, मैं केवल मुख्य बिंदुओं का वर्णन करूंगा:
def process_command(self, command): doc = xml.dom.minidom.parseString(command) url = self._gettext(doc.getElementsByTagName("url")[0].childNodes) try: f = urllib2.urlopen(url) except Exception, err: return ["%s" % str(err)] lines = base64.b64encode(f.read()) ret = [] chunk_size = 1024 x = 0 n = 1 chunk_count = (len(lines) + chunk_size - 1) / chunk_size while x < len(lines): ret.append(self._prepare_chunk(n, chunk_count, lines[x:x + chunk_size])) x += chunk_size n += 1 return ret
प्रक्रिया_कमांड फ़ंक्शन, जैसा कि आप शायद याद करते हैं, हमारे बॉट सर्वर द्वारा कहा जाता है। यह XML अनुरोध को पार्स करता है, यह निर्धारित करता है कि इसे किस URL को अनुरोध करने की आवश्यकता है, और यह
urllib2 के साथ करता है। डाउनलोड को base64 में इनकोड किया गया है ताकि विशेष वर्णों के साथ कोई अनपेक्षित समस्या न हो, और समान भागों में विभाजित हो जाए ताकि संदेश लंबाई सीमा में न चला जाए। फिर प्रत्येक चंक को XML में लपेटा जाता है और बाहर भेजा जाता है।
ग्राहक
ग्राहक, वास्तव में, केवल एक कॉलबैक है, जो बेस 64 से डेटा और डिकोड को चमकता है:
def message_callback(con, msg): global fetcher, output, result if msg.getBody(): message = msg.getBody() chunks, count, data = fetcher.parse_answer(message) output.append(data) if chunks == count: result = base64.b64decode(''.join(output))
प्रतिनिधि
सुरंग को पारदर्शी रूप से उपयोग करने के लिए, एक HTTP प्रॉक्सी लागू किया गया है। प्रॉक्सी सर्वर 3128 / tcp को पोर्ट करने के लिए बाध्य करता है और अनुरोधों की प्रतीक्षा करता है। प्राप्त अनुरोधों को प्रसंस्करण के लिए बॉट सर्वर पर भेजा जाता है, परिणाम को डिकोड किया जाता है और क्लाइंट को भेजा जाता है। ग्राहक अनुप्रयोगों के दृष्टिकोण से, हमारा प्रॉक्सी "साधारण" लोगों से अलग नहीं है।
एक TCP सर्वर बनाने के लिए, मानक लाइब्रेरी से
SocketServer.StreamRequestHandler का उपयोग करें।
class RequestHandler(SocketServer.StreamRequestHandler): def handle(self): data = self.request.recv(1024) method, url, headers = parse_http_request(data) if url is not None: response = fetch_file(server_jid, client_jid, password, url) self.wfile.write(response) self.request.close()
Parse_http_request () फ़ंक्शन एक HTTP अनुरोध पार्स करता है, इसके साथ url, हेडर और http संस्करण
खींचता है;
fetch_file () - bot क्लाइंट का उपयोग करके
url का अनुरोध करता है।
निष्कर्ष
पूर्ण स्रोत कोड
यहां एक तीव्र संग्रह के रूप में उपलब्ध
है (आपको फ़ाइल को चलाने और इसे शेल स्क्रिप्ट के रूप में निष्पादित करने की आवश्यकता है)। बेशक, यह एक पूर्ण अनुप्रयोग की तुलना में एक प्रोटोटाइप का अधिक है, लेकिन प्रोटोटाइप काम कर रहा है और कम से कम छोटी फ़ाइलों को समस्याओं के बिना डाउनलोड किया जा सकता है। यह लेख के मुख्य उद्देश्य के लिए पर्याप्त होना चाहिए: आईएम बॉट के "गैर-इंटरैक्टिव" अनुप्रयोग का प्रदर्शन करने के लिए।
बहुत कुछ है जो परियोजना में सुधार किया जा सकता है - प्रमाणीकरण जोड़ने, अनुरोध प्रकारों के लिए सामान्य समर्थन, प्रदर्शन पर काम करने के लिए। यह बहुत दिलचस्प है कि इस तरह की वास्तुकला के साथ किस तरह का प्रदर्शन प्राप्त किया जा सकता है, जिसका अध्ययन, शायद, मैं जल्द ही उठाऊंगा।