दरअसल, इस विषय का अध्ययन करते समय, कई साइटों को खोजा गया था, लेकिन वास्तव में कहीं भी कुछ भी नहीं समझाया गया था, या जानकारी पुरानी प्रोटोकॉल पर आधारित थी। इस हॉव्टो को बनाने के लिए इसने एक तरह के किक का काम किया। यह सभी संभावित समस्याओं का एक विस्तृत विश्लेषण नहीं होगा, लेकिन सिद्धांत का एक सा और कुछ चीजों का वर्णन है जो किसी के लिए तुच्छ हैं, लेकिन किसी (मेरे जैसे) ने कठिनाइयों का कारण बना और एक समाधान खोजने में समय बर्बाद किया। मुझे आपको तुरंत चेतावनी देनी चाहिए - यह विचार नहीं करता है कि PHP में सॉकेट सर्वर कैसे बढ़ाएं, इंटरनेट पर बल्क में ऐसी जानकारी। मैं इस तथ्य से आगे बढ़ूंगा कि सॉकेट सर्वर पहले से ही मौजूद है और आपको बस इसे सिखाने की जरूरत है कि वेबसैट के जरिए कैसे संवाद किया जाए।
तो, पर्याप्त गीत, अब बात करने के लिए!
सिद्धांत की एक बिट।
हाथ मिलाना
वेब सॉकेट के माध्यम से कनेक्ट करते समय, हेडर को HTTP हेडर, तथाकथित हैंडशेक या, हमारी राय में, "हैंडशेक" की तरह एक्सचेंज किया जाता है।
ग्राहक समान सामग्री का हेडर भेजता है:
HTTP / 1.1 पर जाएं / चैट करें
होस्ट: server.example.com
अपग्रेड: वेबसोकेट
कनेक्शन: अपग्रेड
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ ==
मूल: example.com
Sec-WebSocket-Protocol: चैट, सुपरचैट
Sec-WebSocket-Version: 13
सर्वर को उसे क्या जवाब देना चाहिए:
HTTP / 1.1 101 स्विचिंग प्रोटोकॉल
अपग्रेड: वेबसोकेट
कनेक्शन: अपग्रेड
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK +OO =
Sec-WebSocket-Protocol: चैट
यह साहित्य में लिखा गया है (WebSocket प्रोटोकॉल RFC 6455)। ऐसा लगता है कि जटिल: प्राप्त - उत्तर दिया गया। लेकिन यहां मुझे पहली समस्या थी। सर्वर ने क्लाइंट से हेडर प्राप्त किया, इस पर प्रतिक्रिया दी, लेकिन क्लाइंट ने क्लाइंट की परवाह किए बिना (इस मामले में, ब्राउज़र) जवाब नहीं दिया। मैंने वह सब कुछ आजमाया जो दिमाग के लिए काफी था, कुछ भी मदद नहीं की। यहां टिप मिली थी। मेरी त्रुटि का अर्थ यह था कि ब्राउज़र हेडर को अंतिम रिक्त पंक्ति के साथ स्वीकार करता है, और जब से मैंने इसे नहीं भेजा है (ठीक है, मुझे इसके बारे में एक शब्द भी दस्तावेज में नहीं मिला है), ब्राउज़र हेडर के लिए इंतजार करना जारी रखता है, और यह ईवेंट वेब सॉकेट से जुड़ा हुआ है (WebSocket.onopen) ) "ब्राउज़र में नहीं हुआ। अंत में, मेरा जवाब इस प्रकार था:
$answer = "HTTP/1.1 101 Switching Protocols\r\n" ."Upgrade: websocket\r\n" ."Connection: Upgrade\r\n" ."Sec-WebSocket-Accept: ".$hash."\r\n" ."Sec-WebSocket-Protocol: chat\r\n\r\n"
और ग्राहक ने आखिरकार उसे देखा।
सर्वर हैडर
और अब हम सर्वर की प्रतिक्रिया में शामिल किए जाने के लिए आसानी से आगे बढ़ेंगे।
पहली पंक्ति:
"HTTP / 1.1 101 स्विचिंग प्रोटोकॉल" । यहां कुछ भी बदलने की जरूरत नहीं है। 101 के अलावा किसी भी स्थिति कोड का अर्थ यह होगा कि "हैंडशेक" पूरा नहीं हुआ है।
अपग्रेड और
कनेक्शन लाइनों में, यदि आप
तदनुसार "वेबसोकेट" और
"अपग्रेड" दर्ज नहीं करते हैं, तो क्लाइंट को डिस्कनेक्ट करना होगा। यानी हम भी वैसे ही छोड़ देते हैं जैसा वह था। हालांकि, उदाहरण के लिए, हेडलगिस को
"कनेक्शन: कीप
-जिंदा, अपग्रेड" शीर्षक में भेजा गया था, हो सकता है कि उसे भी इसका उत्तर दिया जाए, लेकिन अभी तक मुझे इसके लिए आवश्यकता नहीं मिली है।
अगला शायद एकमात्र ऐसी रेखा है जहाँ हमें हाथ रखने की आवश्यकता है:
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK + xOo =" । यह रेखा यह घोषणा करती है कि सर्वर कनेक्शन को स्वीकार कर रहा है और एक विशेष तरीके से गणना की गई हैश को क्लाइंट द्वारा प्रेषित कुंजी से
सेक-वेब-सॉकेट-की में भेज रहा है ।
आपकी जरूरत हैश की गणना करने के लिए:
- ग्राहक कुंजी और पूर्वनिर्धारित GUID का संबंध। प्रलेखन के अनुसार, GUID निम्नलिखित पंक्ति है: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11।" मान लीजिए कि हमने पहले ही क्लाइंट कुंजी को निकाल लिया है और चर $ कुंजी में संग्रहीत किया गया है (यदि वे किसी भी तरह से चर में आ गए हैं तो अग्रणी और अनुगामी रिक्त स्थान को निकालना न भूलें)
$hash = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
- प्राप्त स्ट्रिंग से शा 1 की गणना, और परिणाम 20 वर्णों के बाइनरी स्ट्रिंग के रूप में होना चाहिए।
$hash = sha1($hash,true);
- और अंतिम - आधार 64 विधि के साथ हैश एन्कोडिंग
$hash = base64_encode($hash);
चलो अगले सर्वर हेडर लाइन पर चलते हैं, जिसे मैं क्लाइंट को भेजता हूं।
"Sec-WebSocket-Protocol: चैट" एक वैकल्पिक पैरामीटर है, और यह क्लाइंट को बताता है कि सर्वर किस उप-प्रोटोकॉल के साथ संचार करेगा। इस सबप्रोटोकॉल को क्लाइंट द्वारा समर्थित किया जाना चाहिए, और यह क्लाइंट से उसी पैरामीटर में आना चाहिए, हालांकि आग और क्रोम ने हेडर में ऐसे पैरामीटर नहीं भेजे थे जब मैं जुड़ा था।
एक और स्वादिष्ट क्षण है कि मैं प्रलेखन में आया था। सर्वर क्लाइंट को बता सकता है कि वह प्रोटोकॉल के किस संस्करण का समर्थन करता है।
उदाहरण के लिए, एक ग्राहक निम्नलिखित भेजता है:
GET /chat HTTP/1.1 ... Sec-WebSocket-Version: 25
सर्वर इसका जवाब देता है:
HTTP/1.1 400 Bad Request ... Sec-WebSocket-Version: 13, 8, 7
यह संभव है और इसलिए:
HTTP/1.1 400 Bad Request ... Sec-WebSocket-Version: 13 Sec-WebSocket-Version: 8, 7
जिसके बाद क्लाइंट हैंडशेक को दोहराता है, लेकिन प्रोटोकॉल संस्करण के साथ जो सर्वर ने उसे सूचित किया।
GET /chat HTTP/1.1 ... Sec-WebSocket-Version: 13
ग्राहक हैडर
उत्पत्ति और
होस्ट के मापदंडों को छोड़कर क्लाइंट के शीर्षक के बारे में बहुत कुछ नहीं कहा जा सकता है।
होस्ट में सर्वर एड्रेस और पोर्ट होता है जिससे वेब सॉकेट जुड़ा होता है।
उत्पत्ति - एक वैकल्पिक क्षेत्र, आमतौर पर ब्राउज़रों द्वारा उपयोग किया जाता है। वेब सर्वर का नाम शामिल है जिसमें से जावास्क्रिप्ट को सर्वर से कनेक्ट करने के लिए लॉन्च किया गया था (IMHO, ने जांच नहीं की)।
पैकेट विनिमय
यहाँ, ज़ाहिर है, वे बहुत स्मार्ट हैं। दस्तावेज़ में फ़्रेम इस तरह दिखता है:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
मैंने देखा कि अभी तो आलस्य किसी तरह समझ में आया ... लेकिन फिर भी ऐसा था। वैसे, जो लोग इसे विस्तार से समझना चाहते हैं, उनके लिए
इस पृष्ठ पर एक समझदार रूसी-भाषा का विवरण है, हालांकि मूल दस्तावेज में, पूरी तरह से समझना बेहतर है। डिकोडिंग और एन्कोडिंग फ्रेम के लिए, मैंने
यहां तैयार किए गए फ़ंक्शंस
hybi10Decode () और
hybi10Encode () पाए , जो खुद को ठीक से काम करने के लिए दिखाता था।
हैंडशेक () फ़ंक्शन क्लाइंट हेडर पैरामीटर प्राप्त करने के लिए एक विधि का भी वर्णन करता है।
यह भी ध्यान दें कि हैंडशेक के बाद, क्लाइंट सर्वर पर केवल नकाबपोश फ्रेम भेजता है, और सर्वर केवल क्लाइंट को बिना फ्रेम के फ्रेम भेजता है, अर्थात बिट MASK = 0।
इस प्रक्रिया में, मैं एक और समस्या में भाग गया, "हेलो" क्लाइंट को हाथ और सर्वर की प्रतिक्रिया के बाद, क्रोम ने निम्नलिखित उत्पादन किया:
WebSocket कनेक्शन 'ws: //example.com: 10001 / test' में विफल: एक सर्वर को किसी भी फ़्रेम को मास्क नहीं करना चाहिए जो वह क्लाइंट को भेजता है।
यही है, ब्राउज़र ने देखा कि अगला संदेश नकाबपोश था, हालांकि मुझे यह सुनिश्चित करने के लिए पता था कि सर्वर ने एक अनमास्क फ़्रेम भेजा था। एक लंबे खरोंच शलजम के बाद, यह पता चला कि यह फ्लैश के माध्यम से वेब सॉकेट्स और एक्शनस्क्रिप्ट सॉकेट्स के माध्यम से एक्सचेंज प्रोटोकॉल की संगतता की समस्या थी। मेरे कोड में, प्रत्येक संदेश के अंत में फ्लैश के अनुसार शून्य बाइट "\ 0" डाला गया था, और तदनुसार इस बाइट को प्रत्येक फ्रेम या हेडर के अंत में डाला गया था, और ब्राउज़र इसे पहले से ही अगले एक की शुरुआत के रूप में पढ़ता है, क्योंकि ब्राउज़र फ्रेम की सही लंबाई जानता है या जहां अंत है। हैडर। इस प्रकार, अगले हेडर का पहला बाइट "\ 0" था, और वास्तविक पहले को दूसरे में स्थानांतरित कर दिया गया, जिसने ब्राउज़र को अशिष्ट बना दिया।
अभी के लिए बस इतना ही। अंत में, मैं कहना चाहूंगा कि वेब सॉकेट, सामान्य रूप से एचटीएमएल 5 की तरह, एक अद्भुत उपकरण है जो ब्राउज़र को स्वतंत्र रूप से वह करने की अनुमति देगा, जो कि पहले नहीं कर सकता था, बेशक, फ्लैश बैसाखी के बिना।
अद्यतनटिप्पणियों में, यह नोट किया गया कि, विनिर्देश के अनुसार, सभी संदेश utf-8 एन्कोडिंग में भेजे गए हैं। यह भी एक महत्वपूर्ण बिंदु है, लेकिन मैं इसका उल्लेख करना भूल गया।
अपडेट 17-05-13एक और समस्या का सामना करना पड़ा: ब्राउज़र एक पंक्ति में दो फ्रेम भेज सकता है, और उपरोक्त
hybi10Decode () फ़ंक्शन इसे एक फ्रेम के रूप में संसाधित करता है, क्योंकि यह फ्रेम में प्रेषित पेलोड लंबाई के अनुसार लाइन को नहीं पढ़ता है, लेकिन पूरे फ्रेम के अंत तक। कुछ परिवर्तनों के बाद, फ़ंक्शन इस तरह दिखता है:
प्रेस function decode($data){ $payloadLength = ''; $mask = ''; $unmaskedPayload = ''; $decodedData = array();
कृपया ध्यान दें कि यह फ़ंक्शन खंडित फ़्रेम का समर्थन नहीं करता है।
संदर्भ
RFC6455जावास्क्रिप्ट वेबस्केटGitHub प्रोजेक्ट जो प्रोटोकॉल के संस्करण 13 के साथ काम करता है