Python asyncio લો-લેવલ નેટવર્કિંગમાં માસ્ટર બનો. આ ઊંડાણપૂર્વકનો અભ્યાસ ટ્રાન્સપોર્ટ અને પ્રોટોકોલ્સને આવરી લે છે, ઉચ્ચ-પ્રદર્શન, કસ્ટમ નેટવર્ક એપ્લિકેશન્સ બનાવવા માટે વ્યવહારુ ઉદાહરણો સાથે.
Pythonના Asyncio ટ્રાન્સપોર્ટને સમજવું: લો-લેવલ નેટવર્કિંગમાં ઊંડાણપૂર્વકનો અભ્યાસ
આધુનિક Pythonની દુનિયામાં, asyncio
ઉચ્ચ-પ્રદર્શન નેટવર્ક પ્રોગ્રામિંગનો મુખ્ય આધાર બની ગયું છે. ડેવલપર્સ ઘણીવાર તેની સુંદર ઉચ્ચ-સ્તરની API થી શરૂઆત કરે છે, aiohttp
અથવા FastAPI
જેવી લાઇબ્રેરીઓ સાથે async
અને await
નો ઉપયોગ કરીને નોંધપાત્ર સરળતા સાથે પ્રતિભાવશીલ એપ્લિકેશન્સ બનાવે છે. asyncio.open_connection()
જેવા કાર્યો દ્વારા પૂરા પાડવામાં આવતા StreamReader
અને StreamWriter
ઓબ્જેક્ટો, નેટવર્ક I/O ને હેન્ડલ કરવાની અદ્ભુત રીતે સરળ, ક્રમિક રીત પ્રદાન કરે છે. પરંતુ જ્યારે એબ્સ્ટ્રેક્શન પૂરતું ન હોય ત્યારે શું? જો તમારે જટિલ, સ્ટેટફુલ, અથવા નોન-સ્ટાન્ડર્ડ નેટવર્ક પ્રોટોકોલ લાગુ કરવાની જરૂર હોય તો? જો તમારે અંતર્ગત કનેક્શનને સીધું નિયંત્રિત કરીને દરેક અંતિમ પ્રદર્શનને બહાર કાઢવાની જરૂર હોય તો? આ તે છે જ્યાં asyncio ની નેટવર્કિંગ ક્ષમતાઓની સાચી પાયાની રચના રહેલી છે: લો-લેવલ ટ્રાન્સપોર્ટ અને પ્રોટોકોલ API. જ્યારે તે શરૂઆતમાં ડરામણું લાગે છે, ત્યારે આ શક્તિશાળ જોડીને સમજવાથી તમને નિયંત્રણ અને સુગમતાનું નવું સ્તર મળે છે, જે તમને કલ્પના કરી શકાય તેવી લગભગ કોઈપણ નેટવર્ક એપ્લિકેશન બનાવવા સક્ષમ બનાવે છે. આ વ્યાપક માર્ગદર્શિકા એબ્સ્ટ્રેક્શનના સ્તરોને દૂર કરશે, ટ્રાન્સપોર્ટ અને પ્રોટોકોલ્સ વચ્ચેના સહજીવી સંબંધની શોધ કરશે, અને તમને Python માં લો-લેવલ અસુમેળ નેટવર્કિંગમાં નિપુણતા મેળવવા માટે સશક્ત બનાવવા વ્યવહારુ ઉદાહરણો દ્વારા તમને માર્ગદર્શન આપશે.
Asyncio નેટવર્કિંગના બે ચહેરા: ઉચ્ચ-સ્તર વિ લો-સ્તર
લો-લેવલ API માં ઊંડા ઉતરતા પહેલા, asyncio ઇકોસિસ્ટમમાં તેમના સ્થાનને સમજવું અત્યંત મહત્વપૂર્ણ છે. Asyncio બુદ્ધિપૂર્વક નેટવર્ક સંચાર માટે બે અલગ સ્તરો પ્રદાન કરે છે, દરેક જુદા જુદા ઉપયોગના કિસ્સાઓ માટે તૈયાર છે.
ઉચ્ચ-સ્તર API: સ્ટ્રીમ્સ
ઉચ્ચ-સ્તર API, જેને સામાન્ય રીતે "સ્ટ્રીમ્સ" તરીકે ઓળખવામાં આવે છે, તે છે જે મોટાભાગના ડેવલપર્સ પ્રથમ સંપર્કમાં આવે છે. જ્યારે તમે asyncio.open_connection()
અથવા asyncio.start_server()
નો ઉપયોગ કરો છો, ત્યારે તમને StreamReader
અને StreamWriter
ઓબ્જેક્ટો મળે છે. આ API સરળતા અને ઉપયોગમાં સરળતા માટે ડિઝાઇન કરવામાં આવી છે.
- આદેશાત્મક શૈલી: તે તમને ક્રમિક દેખાતો કોડ લખવાની મંજૂરી આપે છે. તમે 100 બાઇટ્સ મેળવવા માટે
await reader.read(100)
કરો છો, પછી પ્રતિભાવ મોકલવા માટેwriter.write(data)
લખો છો. આasync/await
પેટર્ન સાહજિક અને સમજવા માટે સરળ છે. - સુવિધાજનક સહાયકો: તે
readuntil(separator)
અનેreadexactly(n)
જેવી પદ્ધતિઓ પ્રદાન કરે છે જે સામાન્ય ફ્રેમિંગ કાર્યોને હેન્ડલ કરે છે, તમને બફરને મેન્યુઅલી મેનેજ કરવાથી બચાવે છે. - આદર્શ ઉપયોગના કિસ્સાઓ: સરળ વિનંતી-પ્રતિભાવ પ્રોટોકોલ્સ (જેમ કે મૂળભૂત HTTP ક્લાયંટ), લાઇન-આધારિત પ્રોટોકોલ્સ (જેમ કે Redis અથવા SMTP), અથવા કોઈપણ પરિસ્થિતિ જ્યાં સંચાર અનુમાનિત, રેખીય પ્રવાહને અનુસરે છે તેના માટે પરફેક્ટ.
જોકે, આ સરળતા સાથે એક સમાધાન આવે છે. અત્યંત સમકાલીન, ઇવેન્ટ-ડ્રિવન પ્રોટોકોલ્સ માટે સ્ટ્રીમ-આધારિત અભિગમ ઓછો કાર્યક્ષમ હોઈ શકે છે જ્યાં અનિચ્છનીય સંદેશાઓ કોઈપણ સમયે આવી શકે છે. ક્રમિક await
મોડેલ એકસાથે રીડ અને રાઇટ્સને હેન્ડલ કરવા અથવા જટિલ કનેક્શન સ્ટેટ્સ મેનેજ કરવા માટે તેને મુશ્કેલ બનાવી શકે છે.
લો-લેવલ API: ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સ
આ મૂળભૂત સ્તર છે જેના પર ઉચ્ચ-સ્તર સ્ટ્રીમ્સ API ખરેખર બનેલી છે. લો-લેવલ API બે અલગ ઘટકો પર આધારિત ડિઝાઇન પેટર્નનો ઉપયોગ કરે છે: ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સ.
- ઇવેન્ટ-ડ્રિવન શૈલી: તમે ડેટા મેળવવા માટે ફંક્શનને બોલાવો તેના બદલે, ઇવેન્ટ્સ થાય ત્યારે (દા.ત., કનેક્શન મેડ, ડેટા પ્રાપ્ત થયો) asyncio તમારા ઓબ્જેક્ટ પર પદ્ધતિઓને બોલાવે છે. આ એક કોલબેક-આધારિત અભિગમ છે.
- ચિંતાઓની અલગતા: તે "શું" ને "કેવી રીતે" થી સ્પષ્ટપણે અલગ કરે છે. પ્રોટોકોલ ડેટા સાથે શું કરવું તે વ્યાખ્યાયિત કરે છે (તમારી એપ્લિકેશન લોજિક), જ્યારે ટ્રાન્સપોર્ટ નેટવર્ક પર ડેટા કેવી રીતે મોકલવામાં આવે છે અને પ્રાપ્ત થાય છે તે હેન્ડલ કરે છે (I/O મિકેનિઝમ).
- મહત્તમ નિયંત્રણ: આ API તમને બફરિંગ, ફ્લો કંટ્રોલ (બેકપ્રેશર), અને કનેક્શન લાઇફસાયકલ પર ફાઇન-ગ્રેઇન્ડ કંટ્રોલ આપે છે.
- આદર્શ ઉપયોગના કિસ્સાઓ: કસ્ટમ બાઈનરી અથવા ટેક્સ્ટ પ્રોટોકોલ્સ લાગુ કરવા, હજારો સતત કનેક્શન્સને હેન્ડલ કરતા ઉચ્ચ-પ્રદર્શન સર્વર્સ બનાવવા, અથવા નેટવર્ક ફ્રેમવર્ક અને લાઇબ્રેરીઓ વિકસાવવા માટે આવશ્યક.
તેને આ રીતે વિચારો: સ્ટ્રીમ્સ API એ ભોજન કીટ સેવા ઓર્ડર કરવા જેવી છે. તમને પૂર્વ-માપેલા ઘટકો અને અનુસરવા માટે એક સરળ રેસીપી મળે છે. ટ્રાન્સપોર્ટ અને પ્રોટોકોલ API એ પ્રોફેશનલ કિચનમાં કાચા ઘટકો અને પ્રક્રિયાના દરેક પગલા પર સંપૂર્ણ નિયંત્રણ ધરાવતા શેફ બનવા જેવી છે. બંને એક મહાન ભોજન બનાવી શકે છે, પરંતુ પછીનો અનંત સર્જનાત્મકતા અને નિયંત્રણ પ્રદાન કરે છે.
મુખ્ય ઘટકો: ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સ પર નજીકથી નજર
લો-લેવલ API ની શક્તિ પ્રોટોકોલ અને ટ્રાન્સપોર્ટ વચ્ચેના આકર્ષક ક્રિયાપ્રતિક્રિયામાંથી આવે છે. તેઓ કોઈપણ લો-લેવલ asyncio નેટવર્ક એપ્લિકેશનમાં અલગ પરંતુ અવિભાજ્ય ભાગીદારો છે.
પ્રોટોકોલ: તમારી એપ્લિકેશનનું મગજ
પ્રોટોકોલ એ એક વર્ગ છે જે તમે લખો છો. તે asyncio.Protocol
(અથવા તેના પ્રકારોમાંના એક) માંથી વારસાગત થાય છે અને એકલ નેટવર્ક કનેક્શનને હેન્ડલ કરવા માટેની સ્થિતિ અને તર્ક ધરાવે છે. તમે આ વર્ગને જાતે ઇન્સ્ટન્સિએટ કરતા નથી; તમે તેને asyncio ને પ્રદાન કરો છો (દા.ત., loop.create_server
પર), અને asyncio દરેક નવા ક્લાયન્ટ કનેક્શન માટે તમારા પ્રોટોકોલનું નવું ઇન્સ્ટન્સ બનાવે છે.
તમારો પ્રોટોકોલ વર્ગ ઇવેન્ટ હેન્ડલર પદ્ધતિઓના સમૂહ દ્વારા વ્યાખ્યાયિત થયેલ છે જેને ઇવેન્ટ લૂપ કનેક્શનના લાઇફસાયકલના વિવિધ બિંદુઓ પર બોલાવે છે. સૌથી મહત્વપૂર્ણ છે:
connection_made(self, transport)
જ્યારે નવું કનેક્શન સફળતાપૂર્વક સ્થાપિત થાય ત્યારે બરાબર એકવાર બોલાવવામાં આવે છે. આ તમારો પ્રવેશ બિંદુ છે. તે તે છે જ્યાં તમને transport
ઓબ્જેક્ટ મળે છે, જે કનેક્શનનું પ્રતિનિધિત્વ કરે છે. તમારે હંમેશા તેનો સંદર્ભ સાચવવો જોઈએ, સામાન્ય રીતે self.transport
તરીકે. તે કોઈપણ પ્રતિ-કનેક્શન પ્રારંભિકરણ કરવા માટે આદર્શ સ્થાન છે, જેમ કે બફર સેટ કરવા અથવા પીઅરના સરનામાને લોગ કરવું.
data_received(self, data)
તમારા પ્રોટોકોલનું હૃદય. જ્યારે પણ કનેક્શનના બીજા છેડેથી નવો ડેટા પ્રાપ્ત થાય ત્યારે આ પદ્ધતિ બોલાવવામાં આવે છે. data
આર્ગ્યુમેન્ટ એ bytes
ઓબ્જેક્ટ છે. તે યાદ રાખવું નિર્ણાયક છે કે TCP એ સ્ટ્રીમ પ્રોટોકોલ છે, મેસેજ પ્રોટોકોલ નથી. તમારા એપ્લિકેશનનો એકલ તાર્કિક સંદેશ બહુવિધ data_received
કોલ્સ પર વિભાજિત થઈ શકે છે, અથવા બહુવિધ નાના સંદેશાઓ એક કોલમાં બંડલ થઈ શકે છે. તમારા કોડે આ બફરિંગ અને પાર્સિંગને હેન્ડલ કરવું આવશ્યક છે.
connection_lost(self, exc)
જ્યારે કનેક્શન બંધ થાય ત્યારે બોલાવવામાં આવે છે. આ અનેક કારણોસર થઈ શકે છે. જો કનેક્શન સ્વચ્છ રીતે બંધ થાય (દા.ત., બીજો છેડો તેને બંધ કરે છે, અથવા તમે transport.close()
ને બોલાવો છો), તો exc
None
હશે. જો કનેક્શન ભૂલને કારણે બંધ થાય છે (દા.ત., નેટવર્ક નિષ્ફળતા, રીસેટ), તો exc
ભૂલનું વિગતવાર વર્ણન કરતો એક અપવાદ ઓબ્જેક્ટ હશે. આ તમારા માટે સફાઈ કરવા, ડિસ્કનેક્શનને લોગ કરવા, અથવા જો તમે ક્લાયંટ બનાવી રહ્યા હોવ તો ફરીથી કનેક્ટ કરવાનો પ્રયાસ કરવાની તક છે.
eof_received(self)
આ એક વધુ સૂક્ષ્મ કોલબેક છે. જ્યારે બીજો છેડો સંકેત આપે છે કે તે હવે કોઈ ડેટા મોકલશે નહીં (દા.ત., POSIX સિસ્ટમ પર shutdown(SHUT_WR)
બોલાવીને), પરંતુ તમે ડેટા મોકલવા માટે કનેક્શન હજુ પણ ખુલ્લું હોઈ શકે છે. જો તમે આ પદ્ધતિમાંથી True
પરત કરો છો, તો ટ્રાન્સપોર્ટ બંધ થઈ જશે. જો તમે False
(ડિફૉલ્ટ) પરત કરો છો, તો તમે પછીથી ટ્રાન્સપોર્ટને જાતે બંધ કરવા માટે જવાબદાર છો.
ટ્રાન્સપોર્ટ: સંચાર ચેનલ
ટ્રાન્સપોર્ટ એ asyncio દ્વારા પૂરો પાડવામાં આવેલો ઓબ્જેક્ટ છે. તમે તેને બનાવતા નથી; તમને તે તમારા પ્રોટોકોલની connection_made
પદ્ધતિમાં મળે છે. તે અંતર્ગત નેટવર્ક સોકેટ અને ઇવેન્ટ લૂપની I/O શેડ્યૂલિંગ પર ઉચ્ચ-સ્તર એબ્સ્ટ્રેક્શન તરીકે કાર્ય કરે છે. તેનું પ્રાથમિક કાર્ય ડેટા મોકલવાનું અને કનેક્શનનું નિયંત્રણ કરવાનું છે.
તમે તેના પદ્ધતિઓ દ્વારા ટ્રાન્સપોર્ટ સાથે ક્રિયાપ્રતિક્રિયા કરો છો:
transport.write(data)
ડેટા મોકલવા માટેની પ્રાથમિક પદ્ધતિ. data
એ bytes
ઓબ્જેક્ટ હોવો જોઈએ. આ પદ્ધતિ નોન-બ્લોકિંગ છે. તે ડેટા તરત મોકલતી નથી. તેના બદલે, તે ડેટાને આંતરિક રાઇટ બફરમાં મૂકે છે, અને ઇવેન્ટ લૂપ તેને પૃષ્ઠભૂમિમાં શક્ય તેટલી કાર્યક્ષમ રીતે નેટવર્ક પર મોકલે છે.
transport.writelines(list_of_data)
એક સાથે બફરમાં bytes
ઓબ્જેક્ટોના ક્રમ લખવાની વધુ કાર્યક્ષમ રીત, સંભવિતપણે સિસ્ટમ કોલ્સની સંખ્યા ઘટાડે છે.
transport.close()
આ એક ગ્રેસફુલ શટડાઉન શરૂ કરે છે. ટ્રાન્સપોર્ટ સૌ પ્રથમ તેના રાઇટ બફરમાં બાકી રહેલા કોઈપણ ડેટાને ફ્લશ કરશે અને પછી કનેક્શન બંધ કરશે. close()
બોલાવ્યા પછી વધુ ડેટા લખી શકાતો નથી.
transport.abort()
આ એક હાર્ડ શટડાઉન કરે છે. કનેક્શન તરત જ બંધ થઈ જાય છે, અને રાઇટ બફરમાં બાકી રહેલો કોઈપણ ડેટા કાઢી નાખવામાં આવે છે. આ અસાધારણ સંજોગોમાં વાપરવો જોઈએ.
transport.get_extra_info(name, default=None)
નિરીક્ષણ માટે ખૂબ ઉપયોગી પદ્ધતિ. તમે કનેક્શન વિશેની માહિતી મેળવી શકો છો, જેમ કે પીઅરનું સરનામું ('peername'
), અંતર્ગત સોકેટ ઓબ્જેક્ટ ('socket'
), અથવા SSL/TLS પ્રમાણપત્ર માહિતી ('ssl_object'
).
સહજીવી સંબંધ
આ ડિઝાઇનનું સૌંદર્ય માહિતીના સ્પષ્ટ, ચક્રીય પ્રવાહમાં રહેલું છે:
- સેટઅપ: ઇવેન્ટ લૂપ નવું કનેક્શન સ્વીકારે છે.
- ઇન્સ્ટન્સિએશન: લૂપ તમારા
Protocol
વર્ગનું ઇન્સ્ટન્સ અને કનેક્શનનું પ્રતિનિધિત્વ કરતોTransport
ઓબ્જેક્ટ બનાવે છે. - જોડાણ: લૂપ
your_protocol.connection_made(transport)
ને બોલાવે છે, બે ઓબ્જેક્ટોને એકસાથે જોડે છે. તમારા પ્રોટોકોલ પાસે હવે ડેટા મોકલવાનો માર્ગ છે. - ડેટા પ્રાપ્ત કરવો: જ્યારે નેટવર્ક સોકેટ પર ડેટા આવે છે, ત્યારે ઇવેન્ટ લૂપ જાગે છે, ડેટા વાંચે છે, અને
your_protocol.data_received(data)
ને બોલાવે છે. - પ્રક્રિયા: તમારા પ્રોટોકોલનું લોજિક પ્રાપ્ત થયેલા ડેટાને પ્રોસેસ કરે છે.
- ડેટા મોકલવો: તેના લોજિકના આધારે, તમારો પ્રોટોકોલ પ્રતિભાવ ડેટા મોકલવા માટે
self.transport.write(response_data)
ને બોલાવે છે. ડેટા બફર થયેલ છે. - પૃષ્ઠભૂમિ I/O: ઇવેન્ટ લૂપ ટ્રાન્સપોર્ટ પર બફર થયેલા ડેટાના નોન-બ્લોકિંગ મોકલવાનું હેન્ડલ કરે છે.
- ટેરડાઉન: જ્યારે કનેક્શન સમાપ્ત થાય છે, ત્યારે ઇવેન્ટ લૂપ અંતિમ સફાઈ માટે
your_protocol.connection_lost(exc)
ને બોલાવે છે.
વ્યવહારુ ઉદાહરણ બનાવવું: એક ઇકો સર્વર અને ક્લાયંટ
સિદ્ધાંત મહાન છે, પરંતુ ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સને સમજવાની શ્રેષ્ઠ રીત કંઈક બનાવવી છે. ચાલો એક ક્લાસિક ઇકો સર્વર અને અનુરૂપ ક્લાયંટ બનાવીએ. સર્વર કનેક્શન્સ સ્વીકારશે અને ફક્ત જે ડેટા પ્રાપ્ત કરે છે તેને પાછો મોકલશે.
ઇકો સર્વર અમલીકરણ
પ્રથમ, આપણે આપણા સર્વર-સાઇડ પ્રોટોકોલને વ્યાખ્યાયિત કરીશું. તે અદ્ભુત રીતે સરળ છે, મુખ્ય ઇવેન્ટ હેન્ડલર્સ દર્શાવે છે.
import asyncio
class EchoServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
# એક નવું કનેક્શન સ્થાપિત થયું છે.
# લોગિંગ માટે રિમોટ એડ્રેસ મેળવો.
peername = transport.get_extra_info('peername')
print(f"Connection from: {peername}")
# ભવિષ્યમાં ઉપયોગ માટે ટ્રાન્સપોર્ટ સ્ટોર કરો.
self.transport = transport
def data_received(self, data):
# ક્લાયંટ પાસેથી ડેટા પ્રાપ્ત થયો છે.
message = data.decode()
print(f"Data received: {message.strip()}")
# ડેટા ક્લાયંટને પાછો ઇકો કરો.
print(f"Echoing back: {message.strip()}")
self.transport.write(data)
def connection_lost(self, exc):
# કનેક્શન બંધ થઈ ગયું છે.
print("Connection closed.")
# ટ્રાન્સપોર્ટ આપમેળે બંધ થઈ જાય છે, અહીં self.transport.close() બોલાવવાની જરૂર નથી.
async def main_server():
# ઇવેન્ટ લૂપનો સંદર્ભ મેળવો કારણ કે આપણે સર્વરને અનિશ્ચિત રૂપે ચલાવવાની યોજના બનાવી રહ્યા છીએ.
loop = asyncio.get_running_loop()
host = '127.0.0.1'
port = 8888
# `create_server` કોર્યુટિન સર્વર બનાવે છે અને શરૂ કરે છે.
# પ્રથમ આર્ગ્યુમેન્ટ protocol_factory છે, જે નવું પ્રોટોકોલ ઇન્સ્ટન્સ પરત કરે છે.
# અમારા કિસ્સામાં, ફક્ત `EchoServerProtocol` વર્ગ પાસ કરવો કામ કરે છે.
server = await loop.create_server(
lambda: EchoServerProtocol(),
host,
port)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addrs}')
# સર્વર પૃષ્ઠભૂમિમાં ચાલે છે. મુખ્ય કોર્યુટિનને જીવંત રાખવા માટે,
# આપણે કંઈક અપેક્ષા રાખી શકીએ છીએ જે ક્યારેય પૂર્ણ થતું નથી, જેમ કે નવું Future.
# આ ઉદાહરણ માટે, આપણે તેને ફક્ત "હંમેશા" ચલાવીશું.
async with server:
await server.serve_forever()
if __name__ == "__main__":
try:
# સર્વર ચલાવવા માટે:
asyncio.run(main_server())
except KeyboardInterrupt:
print("Server shut down.")
આ સર્વર કોડમાં, loop.create_server()
મુખ્ય છે. તે ઉલ્લેખિત હોસ્ટ અને પોર્ટ પર બંધાય છે અને ઇવેન્ટ લૂપને નવા કનેક્શન્સ માટે સાંભળવાનું શરૂ કરવા કહે છે. દરેક આવતા કનેક્શન માટે, તે તે ચોક્કસ ક્લાયંટને સમર્પિત તાજા પ્રોટોકોલ ઇન્સ્ટન્સ બનાવવા માટે અમારી protocol_factory
(lambda: EchoServerProtocol()
ફંક્શન) ને બોલાવે છે.
ઇકો ક્લાયંટ અમલીકરણ
ક્લાયંટ પ્રોટોકોલ થોડો વધુ સામેલ છે કારણ કે તેને તેની પોતાની સ્થિતિ મેનેજ કરવાની જરૂર છે: કયો સંદેશ મોકલવો અને જ્યારે તેનું કાર્ય "પૂર્ણ" ગણવામાં આવે છે. એક સામાન્ય પદ્ધતિ એ છે કે ક્લાયંટને શરૂ કરનાર મુખ્ય કોર્યુટિન પર પૂર્ણતાનો સંકેત આપવા માટે asyncio.Future
અથવા asyncio.Event
નો ઉપયોગ કરવો.
import asyncio
class EchoClientProtocol(asyncio.Protocol):
def __init__(self, message, on_con_lost):
self.message = message
self.on_con_lost = on_con_lost
self.transport = None
def connection_made(self, transport):
self.transport = transport
print(f"Sending: {self.message}")
self.transport.write(self.message.encode())
def data_received(self, data):
print(f"Received echo: {data.decode().strip()}")
def connection_lost(self, exc):
print("The server closed the connection")
# સંકેત આપો કે કનેક્શન ખોવાઈ ગયું છે અને કાર્ય પૂર્ણ થયું છે.
self.on_con_lost.set_result(True)
def eof_received(self):
# જો સર્વર બંધ કરતા પહેલા EOF મોકલે તો આ બોલાવી શકાય છે.
print("Received EOF from server.")
async def main_client():
loop = asyncio.get_running_loop()
# on_con_lost Future નો ઉપયોગ ક્લાયંટના કાર્યની પૂર્ણતાનો સંકેત આપવા માટે થાય છે.
on_con_lost = loop.create_future()
message = "Hello World!"
host = '127.0.0.1'
port = 8888
# `create_connection` કનેક્શન સ્થાપિત કરે છે અને પ્રોટોકોલને લિંક કરે છે.
try:
transport, protocol = await loop.create_connection(
lambda: EchoClientProtocol(message, on_con_lost),
host,
port)
except ConnectionRefusedError:
print("Connection refused. Is the server running?")
return
# જ્યાં સુધી પ્રોટોકોલ સંકેત ન આપે ત્યાં સુધી રાહ જુઓ કે કનેક્શન ખોવાઈ ગયું છે.
try:
await on_con_lost
finally:
# ગ્રેસફુલી ટ્રાન્સપોર્ટ બંધ કરો.
transport.close()
if __name__ == "__main__":
# ક્લાયંટ ચલાવવા માટે:
# પ્રથમ, એક ટર્મિનલમાં સર્વર શરૂ કરો.
# પછી, બીજી ટર્મિનલમાં આ સ્ક્રિપ્ટ ચલાવો.
asyncio.run(main_client())
અહીં, loop.create_connection()
એ create_server
નો ક્લાયંટ-સાઇડ પ્રતિરૂપ છે. તે આપેલા સરનામા સાથે કનેક્ટ કરવાનો પ્રયાસ કરે છે. જો સફળ થાય, તો તે અમારા EchoClientProtocol
ને ઇન્સ્ટન્સિએટ કરે છે અને તેની connection_made
પદ્ધતિને બોલાવે છે. on_con_lost
Future નો ઉપયોગ એક નિર્ણાયક પેટર્ન છે. main_client
કોર્યુટિન આ ફ્યુચરની await
કરે છે, અસરકારક રીતે તેના પોતાના અમલને ત્યારે સુધી રોકી રાખે છે જ્યાં સુધી પ્રોટોકોલ connection_lost
ની અંદર on_con_lost.set_result(True)
બોલાવીને તેના કાર્ય પૂર્ણ થયાનો સંકેત ન આપે.
અદ્યતન ખ્યાલો અને વાસ્તવિક-વિશ્વના દૃશ્યો
ઇકો ઉદાહરણ મૂળભૂત બાબતોને આવરી લે છે, પરંતુ વાસ્તવિક-વિશ્વના પ્રોટોકોલ્સ ભાગ્યે જ એટલા સરળ હોય છે. ચાલો કેટલાક વધુ અદ્યતન વિષયોનું અન્વેષણ કરીએ જેનો તમે અનિવાર્યપણે સામનો કરશો.
મેસેજ ફ્રેમિંગ અને બફરિંગ હેન્ડલ કરવું
મૂળભૂત બાબતો પછી સમજવા માટેનો સૌથી મહત્વપૂર્ણ ખ્યાલ એ છે કે TCP એ બાઇટ્સની સ્ટ્રીમ છે. ત્યાં કોઈ આંતરિક "મેસેજ" સીમાઓ નથી. જો ક્લાયંટ "Hello" અને પછી "World" મોકલે છે, તો તમારા સર્વરના data_received
ને b'HelloWorld'
સાથે એકવાર, b'Hello'
અને b'World'
સાથે બે વાર, અથવા આંશિક ડેટા સાથે બહુવિધ વખત બોલાવી શકાય છે.
તમારો પ્રોટોકોલ "ફ્રેમિંગ" માટે જવાબદાર છે - આ બાઇટ સ્ટ્રીમ્સને અર્થપૂર્ણ સંદેશાઓમાં ફરીથી જોડવું. એક સામાન્ય વ્યૂહરચના એ ડિલિમિટરનો ઉપયોગ કરવાનો છે, જેમ કે નવી લાઇન અક્ષર (
).
અહીં એક સુધારેલો પ્રોટોકોલ છે જે નવી લાઇન ન મળે ત્યાં સુધી ડેટા બફર કરે છે, એક સમયે એક લીટી પર પ્રક્રિયા કરે છે.
class LineBasedProtocol(asyncio.Protocol):
def __init__(self):
self._buffer = b''
self.transport = None
def connection_made(self, transport):
self.transport = transport
print("Connection established.")
def data_received(self, data):
# આંતરિક બફરમાં નવો ડેટા ઉમેરો
self._buffer += data
# બફરમાં જેટલી સંપૂર્ણ લાઇનો છે તેટલી પ્રોસેસ કરો
while b'\n' in self._buffer:
line, self._buffer = self._buffer.split(b'\n', 1)
self.process_line(line.decode().strip())
def process_line(self, line):
# આ તે છે જ્યાં એકલ સંદેશ માટે તમારી એપ્લિકેશન લોજિક જાય છે
print(f"Processing complete message: {line}")
response = f"Processed: {line}\n"
self.transport.write(response.encode())
def connection_lost(self, exc):
print("Connection lost.")
ફ્લો કંટ્રોલ (બેકપ્રેશર) મેનેજ કરવું
જો તમારી એપ્લિકેશન ટ્રાન્સપોર્ટ પર નેટવર્ક અથવા રિમોટ પીઅર હેન્ડલ કરી શકે તેના કરતા ઝડપી ડેટા લખી રહી હોય તો શું થાય? ડેટા ટ્રાન્સપોર્ટના આંતરિક બફરમાં એકઠો થાય છે. જો આ અનિયંત્રિત ચાલુ રહે, તો બફર અનિશ્ચિત રૂપે વધી શકે છે, બધી ઉપલબ્ધ મેમરીનો ઉપયોગ કરી શકે છે. આ સમસ્યા "બેકપ્રેશર" ના અભાવ તરીકે ઓળખાય છે.
Asyncio આને હેન્ડલ કરવા માટે એક પદ્ધતિ પ્રદાન કરે છે. ટ્રાન્સપોર્ટ તેના પોતાના બફર કદનું નિરીક્ષણ કરે છે. જ્યારે બફર ચોક્કસ હાઇ-વોટર માર્ક કરતાં વધી જાય છે, ત્યારે ઇવેન્ટ લૂપ તમારા પ્રોટોકોલની pause_writing()
પદ્ધતિને બોલાવે છે. આ તમારી એપ્લિકેશન માટે ડેટા મોકલવાનું બંધ કરવાનો સંકેત છે. જ્યારે બફર લો-વોટર માર્ક કરતા નીચે ગટર થઈ જાય છે, ત્યારે લૂપ resume_writing()
ને બોલાવે છે, જે સંકેત આપે છે કે ડેટા ફરીથી મોકલવો સલામત છે.
class FlowControlledProtocol(asyncio.Protocol):
def __init__(self):
self._paused = False
self._data_source = some_data_generator() # ડેટા જનરેટરની કલ્પના કરો
self.transport = None
def connection_made(self, transport):
self.transport = transport
self.resume_writing() # લખવાની પ્રક્રિયા શરૂ કરો
def pause_writing(self):
# ટ્રાન્સપોર્ટ બફર ભરેલું છે.
print("Pausing writing.")
self._paused = True
def resume_writing(self):
# ટ્રાન્સપોર્ટ બફર ગટર થઈ ગયું છે.
print("Resuming writing.")
self._paused = False
self._write_more_data()
def _write_more_data(self):
# આ આપણી એપ્લિકેશનનો રાઇટ લૂપ છે.
while not self._paused:
try:
data = next(self._data_source)
self.transport.write(data)
except StopIteration:
self.transport.close()
break # મોકલવા માટે કોઈ ડેટા નથી
# બફર કદ તપાસો કે આપણે તરત જ રોકાવવું જોઈએ કે નહીં
if self.transport.get_write_buffer_size() > 0:
self.pause_writing()
TCP થી પરે: અન્ય ટ્રાન્સપોર્ટ્સ
- UDP: કનેક્શનલેસ સંચાર માટે, તમે
loop.create_datagram_endpoint()
નો ઉપયોગ કરો છો. આ તમનેDatagramTransport
આપે છે અને તમેdatagram_received(data, addr)
અનેerror_received(exc)
જેવી પદ્ધતિઓ સાથેasyncio.DatagramProtocol
લાગુ કરશો. - SSL/TLS: એન્ક્રિપ્શન ઉમેરવું અત્યંત સરળ છે. તમે
ssl.SSLContext
ઓબ્જેક્ટનેloop.create_server()
અથવાloop.create_connection()
પર પાસ કરો છો. Asyncio આપમેળે TLS હેન્ડશેકને હેન્ડલ કરે છે, અને તમને સુરક્ષિત ટ્રાન્સપોર્ટ મળે છે. તમારા પ્રોટોકોલ કોડને બિલકુલ બદલવાની જરૂર નથી. - સબપ્રોસેસ: તેમના સ્ટાન્ડર્ડ I/O પાઇપ્સ દ્વારા ચાઇલ્ડ પ્રોસેસ સાથે સંચાર કરવા માટે,
loop.subprocess_exec()
અનેloop.subprocess_shell()
નો ઉપયોગasyncio.SubprocessProtocol
સાથે કરી શકાય છે. આ તમને સંપૂર્ણપણે અસુમેળ, નોન-બ્લોકિંગ રીતે ચાઇલ્ડ પ્રોસેસને મેનેજ કરવાની મંજૂરી આપે છે.
વ્યૂહાત્મક નિર્ણય: ટ્રાન્સપોર્ટ્સ વિ સ્ટ્રીમ્સ ક્યારે વાપરવા
તમારી પાસે બે શક્તિશાળી API ઉપલબ્ધ હોવાથી, નોકરી માટે યોગ્ય પસંદ કરવું એ મુખ્ય આર્કિટેક્ચરલ નિર્ણય છે. અહીં તમને નિર્ણય લેવામાં મદદ કરવા માટે એક માર્ગદર્શિકા છે.
સ્ટ્રીમ્સ (StreamReader
/StreamWriter
) પસંદ કરો જ્યારે...
- તમારો પ્રોટોકોલ સરળ અને વિનંતી-પ્રતિભાવ આધારિત છે. જો લોજિક "એક વિનંતી વાંચો, તેને પ્રોસેસ કરો, એક પ્રતિભાવ લખો" હોય, તો સ્ટ્રીમ્સ પરફેક્ટ છે.
- તમે જાણીતા, લાઇન-આધારિત અથવા નિશ્ચિત-લંબાઈના મેસેજ પ્રોટોકોલ માટે ક્લાયંટ બનાવી રહ્યા છો. ઉદાહરણ તરીકે, Redis સર્વર અથવા સરળ FTP સર્વર સાથે ક્રિયાપ્રતિક્રિયા કરવી.
- તમે કોડ વાંચનીયતા અને રેખીય, આદેશાત્મક શૈલીને પ્રાધાન્ય આપો છો. સ્ટ્રીમ્સ સાથે
async/await
સિન્ટેક્સ અસુમેળ પ્રોગ્રામિંગમાં નવા આવનારાઓ માટે સમજવા માટે ઘણીવાર સરળ હોય છે. - ઝડપી પ્રોટોટાઇપિંગ મુખ્ય છે. તમે સ્ટ્રીમ્સ સાથે ફક્ત થોડી લીટીઓના કોડમાં એક સરળ ક્લાયંટ અથવા સર્વર ચાલુ કરી શકો છો.
ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સ પસંદ કરો જ્યારે...
- તમે શરૂઆતથી જટિલ અથવા કસ્ટમ નેટવર્ક પ્રોટોકોલ લાગુ કરી રહ્યા છો. આ પ્રાથમિક ઉપયોગનો કેસ છે. ગેમિંગ, ફાઇનાન્સિયલ ડેટા ફીડ, IoT ઉપકરણો, અથવા પીઅર-ટુ-પીઅર એપ્લિકેશન્સ માટે પ્રોટોકોલ્સ વિશે વિચારો.
- તમારો પ્રોટોકોલ અત્યંત ઇવેન્ટ-ડ્રિવન છે અને સંપૂર્ણપણે વિનંતી-પ્રતિભાવ નથી. જો સર્વર કોઈપણ સમયે ક્લાયંટને અનિચ્છનીય સંદેશાઓ મોકલી શકે છે, તો પ્રોટોકોલ્સનો કોલબેક-આધારિત સ્વભાવ વધુ કુદરતી ફિટ છે.
- તમારે મહત્તમ પ્રદર્શન અને ન્યૂનતમ ઓવરહેડની જરૂર છે. પ્રોટોકોલ્સ તમને ઇવેન્ટ લૂપ સુધી વધુ સીધો માર્ગ આપે છે, સ્ટ્રીમ્સ API સાથે સંકળાયેલા કેટલાક ઓવરહેડને બાયપાસ કરે છે.
- તમારે કનેક્શન પર ફાઇન-ગ્રેઇન્ડ કંટ્રોલની જરૂર છે. આમાં મેન્યુઅલ બફર મેનેજમેન્ટ, સ્પષ્ટ ફ્લો કંટ્રોલ (
pause/resume_writing
), અને કનેક્શન લાઇફસાયકલનું વિગતવાર હેન્ડલિંગ શામેલ છે. - તમે નેટવર્ક ફ્રેમવર્ક અથવા લાઇબ્રેરી બનાવી રહ્યા છો. જો તમે અન્ય ડેવલપર્સ માટે સાધન પ્રદાન કરી રહ્યા છો, તો પ્રોટોકોલ/ટ્રાન્સપોર્ટ API ની મજબૂત અને લવચીક પ્રકૃતિ ઘણીવાર યોગ્ય પાયો હોય છે.
નિષ્કર્ષ: Asyncio ના પાયાને સ્વીકારવું
Python ની asyncio
લાઇબ્રેરી સ્તરીય ડિઝાઇનનો એક ઉત્કૃષ્ટ નમૂનો છે. જ્યારે ઉચ્ચ-સ્તર સ્ટ્રીમ્સ API એક સુલભ અને ઉત્પાદક પ્રવેશ બિંદુ પ્રદાન કરે છે, ત્યારે તે લો-લેવલ ટ્રાન્સપોર્ટ અને પ્રોટોકોલ API છે જે asyncio ની નેટવર્કિંગ ક્ષમતાઓની સાચી, શક્તિશાળી પાયાનું પ્રતિનિધિત્વ કરે છે. I/O મિકેનિઝમ (ટ્રાન્સપોર્ટ) ને એપ્લિકેશન લોજિક (પ્રોટોકોલ) થી અલગ કરીને, તે અત્યાધુનિક નેટવર્ક એપ્લિકેશન્સ બનાવવા માટે મજબૂત, સ્કેલેબલ અને અત્યંત લવચીક મોડેલ પ્રદાન કરે છે.
આ લો-લેવલ એબ્સ્ટ્રેક્શનને સમજવું એ માત્ર શૈક્ષણિક કસરત નથી; તે એક વ્યવહારુ કૌશલ્ય છે જે તમને સરળ ક્લાયંટ અને સર્વર્સથી આગળ વધવા માટે સશક્ત બનાવે છે. તે તમને કોઈપણ નેટવર્ક પ્રોટોકોલનો સામનો કરવાનો આત્મવિશ્વાસ, દબાણ હેઠળ પ્રદર્શનને ઑપ્ટિમાઇઝ કરવાનો નિયંત્રણ, અને Python માં ઉચ્ચ-પ્રદર્શન, અસુમેળ સેવાઓની આગામી પેઢી બનાવવાની ક્ષમતા આપે છે. આગલી વખતે જ્યારે તમે પડકારરૂપ નેટવર્કિંગ સમસ્યાનો સામનો કરો છો, ત્યારે સપાટીની નીચે રહેલી શક્તિને યાદ રાખો, અને ટ્રાન્સપોર્ટ્સ અને પ્રોટોકોલ્સના આકર્ષક જોડી તરફ પહોંચવામાં અચકાવું નહીં.