सॉकेट लाइब्रेरी में recv को कॉल करते समय मेरा recv बफर कितना बड़ा होना चाहिए


129

सी में सॉकेट लाइब्रेरी के बारे में मेरे कुछ प्रश्न हैं। यहाँ कोड का एक स्निपेट है जिसे मैं अपने प्रश्नों में संदर्भित करूँगा।

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. मैं कैसे तय करूं कि recv_buffer कितना बड़ा है? मैं 3000 का उपयोग कर रहा हूं, लेकिन यह मनमाना है।
  2. यदि recv()मेरे बफ़र से बड़ा पैकेट मिलता है तो क्या होगा ?
  3. अगर मैं फिर से कॉल किए बिना पूरे संदेश को प्राप्त कर लेता हूं तो मुझे कैसे पता चलेगा कि क्या हमेशा के लिए इंतजार करना पड़ता है, जब कुछ प्राप्त नहीं होता है?
  4. क्या कोई ऐसा तरीका है जिससे मैं एक बफर बना सकता हूं जिसमें एक निश्चित मात्रा में स्थान नहीं है, ताकि मैं अंतरिक्ष से बाहर भागने के डर के बिना इसे जोड़कर रख सकूं? शायद बफर के लिए strcatनवीनतम recv()प्रतिक्रिया का उपयोग करने के लिए?

मुझे पता है कि यह एक में बहुत सारे सवाल हैं, लेकिन मैं किसी भी प्रतिक्रिया की बहुत सराहना करूंगा।

जवाबों:


230

इन प्रश्नों के उत्तर इस बात पर निर्भर करते हैं कि क्या आप एक स्ट्रीम सॉकेट ( SOCK_STREAM) या डेटाग्राम सॉकेट ( SOCK_DGRAM) का उपयोग कर रहे हैं - टीसीपी / आईपी के भीतर, पूर्व टीसीपी से संबंधित है और बाद में यूडीपी से।

आपको कैसे पता चलेगा कि बफर बनाने के लिए कितना बड़ा है recv()?

  • SOCK_STREAM: यह वास्तव में बहुत ज्यादा मायने नहीं रखता है। यदि आपका प्रोटोकॉल एक ट्रांसेक्शनल / इंटरएक्टिव है, तो बस एक आकार चुनें जो सबसे बड़ा व्यक्तिगत संदेश / कमांड पकड़ सकता है जो आपको उचित रूप से उम्मीद करेगा (3000 संभावना है कि ठीक है)। यदि आपका प्रोटोकॉल बल्क डेटा स्थानांतरित कर रहा है, तो बड़े बफ़र्स अधिक कुशल हो सकते हैं - अंगूठे का एक अच्छा नियम लगभग उसी तरह है जैसे कर्नेल को सॉकेट का बफर आकार प्राप्त होता है (अक्सर 256kB के आसपास कुछ)।

  • SOCK_DGRAM: एक बड़े बफर का उपयोग करें जो आपके एप्लिकेशन-स्तरीय प्रोटोकॉल को कभी भी भेजे जाने वाले सबसे बड़े पैकेट को पकड़ सके। यदि आप यूडीपी का उपयोग कर रहे हैं, तो सामान्य रूप से आपके एप्लिकेशन-स्तरीय प्रोटोकॉल को लगभग 1400 बाइट्स से बड़ा पैकेट नहीं भेजा जाना चाहिए, क्योंकि उन्हें निश्चित रूप से खंडित और आश्वस्त होना होगा।

यदि recvबफर से बड़ा पैकेट मिलता है तो क्या होता है ?

  • SOCK_STREAM: सवाल वास्तव में पुट के रूप में समझ में नहीं आता है, क्योंकि स्ट्रीम सॉकेट में पैकेट की अवधारणा नहीं है - वे बाइट्स की एक सतत स्ट्रीम हैं। यदि आपके बफ़र के लिए पढ़ने के लिए अधिक बाइट उपलब्ध हैं, तो वे OS द्वारा कतारबद्ध हो जाएंगे और आपके अगले कॉल के लिए उपलब्ध होंगे recv

  • SOCK_DGRAM: अतिरिक्त बाइट्स को छोड़ दिया जाता है।

मुझे कैसे पता चलेगा कि मुझे पूरा संदेश मिला है?

  • SOCK_STREAM: आपको अपने एप्लिकेशन-स्तरीय प्रोटोकॉल में संदेश के अंत का निर्धारण करने के कुछ तरीके बनाने की आवश्यकता है। आम तौर पर यह या तो एक लंबाई उपसर्ग होता है (प्रत्येक संदेश की लंबाई के साथ संदेश शुरू करना) या एक अंत संदेश का सीमांकक (जो उदाहरण के लिए, केवल पाठ-आधारित प्रोटोकॉल में एक नई पंक्ति हो सकता है)। एक तिहाई, कम उपयोग किया जाता है, विकल्प प्रत्येक संदेश के लिए एक निश्चित आकार को जनादेश देना है। इन विकल्पों के संयोजन भी संभव हैं - उदाहरण के लिए, एक निश्चित आकार के हेडर जिसमें लंबाई मान शामिल है।

  • SOCK_DGRAM: एक एकल recvकॉल हमेशा एक एकल डेटाग्राम देता है।

क्या कोई ऐसा तरीका है जिससे मैं एक बफर बना सकता हूं जिसमें एक निश्चित मात्रा में जगह नहीं है, ताकि मैं अंतरिक्ष से बाहर भागने के डर के बिना इसे जोड़कर रख सकूं?

हालाँकि, आप बफर का उपयोग करके आकार बदलने की कोशिश कर सकते हैं realloc()(यदि यह मूल रूप से malloc()या इसके साथ आवंटित किया गया था calloc())।


1
मेरे पास प्रोटोकॉल में एक संदेश के अंत में "/ r / n / r / n" है जो मैं उपयोग कर रहा हूं। और मेरे पास एक लूप है, जबकि मैं अंदर recv कह रहा हूं मैं संदेश को recv_buffer की शुरुआत में रखता हूं। और मेरा समय कथन इस तरह से दिखाई देता है (((((recv_buffer, "\ r \ n \ r \ n")); मेरा प्रश्न यह है कि क्या एक पुनरावृत्ति के लिए "\ r \ n" प्राप्त करना संभव है और अगली रिकव "\ r \ n" प्राप्त करें, ताकि मेरी समय पर स्थिति कभी भी सच न हो?
एडहेलॉन

3
हाँ यही है। यदि आपके पास एक पूर्ण संदेश नहीं है और recvआंशिक संदेश के बाद बफ़र में अगले से बाइट्स भरकर, आप उस समस्या को हल कर सकते हैं । आपको strstr()भरे हुए बफर पर उपयोग नहीं करना चाहिए recv()- इसमें कोई गारंटी नहीं है कि इसमें एक शून्य-टर्मिनेटर है, इसलिए यह strstr()दुर्घटना का कारण बन सकता है ।
कैफे

3
यूडीपी के मामले में, 1400 बाइट्स के ऊपर यूडीपी पैकेट भेजने में कुछ भी गलत नहीं है। विखंडन पूरी तरह से कानूनी है और आईपी प्रोटोकॉल का एक बुनियादी हिस्सा है (यहां तक ​​कि आईपीवी 6 में, फिर भी हमेशा शुरुआती प्रेषक को विखंडन करना चाहिए)। यूडीपी के लिए यदि आप 64 केबी के बफर का उपयोग करते हैं तो आप हमेशा बचते हैं, क्योंकि कोई भी आईपी पैकेट (वी 4 या वी 6) आकार में 64 केबी से ऊपर नहीं हो सकता है (खंडित होने पर भी नहीं) और इसमें हेडर IIRC भी शामिल है, इसलिए डेटा हमेशा रहेगा नीचे 64 केबी यकीन के लिए।
मेक्सी

1
@caf आपको recv () के लिए प्रत्येक कॉल पर बफर को खाली करने की आवश्यकता है? मैं कोड लूप देख रहा हूं और डेटा एकत्र करता हूं और इसे फिर से लूप करता हूं जो अधिक डेटा एकत्र करना चाहिए। लेकिन अगर बफर कभी भर जाता है तो आपको बफर के लिए आवंटित मेमोरी की मात्रा को लिखने के कारण मेमोरी उल्लंघन से बचने के लिए इसे खाली करने की आवश्यकता नहीं है?
अलेक्सि_नबु Alex

1
@Alex_Nabu: आपको इसे तब तक खाली करने की आवश्यकता नहीं है जब तक कि इसमें कुछ जगह शेष है, और आप recv()अंतरिक्ष में शेष जगह की तुलना में अधिक बाइट लिखने के लिए नहीं कहते हैं ।
कैफे

16

स्ट्रीमिंग प्रोटोकॉल जैसे कि टीसीपी के लिए, आप अपने बफर को किसी भी आकार में सेट कर सकते हैं। कहा कि, सामान्य मान जो 2 की शक्तियां हैं जैसे कि 4096 या 8192 की सिफारिश की जाती है।

यदि अधिक डेटा है तो आपका बफ़र क्या है, यह आपके अगले कॉल के लिए कर्नेल में सहेजा जाएगा recv

हां, आप अपने बफर को बढ़ाते रह सकते हैं। आप ऑफसेट पर शुरू होने वाले बफर के बीच में एक रिकव कर सकते हैं idx, आप यह करेंगे:

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

6
दो की शक्ति कई मायनों में अधिक कुशल हो सकती है, और दृढ़ता से सुझाई जाती है।
यमन रामिन

3
@theatrus पर विस्तार से, एक उल्लेखनीय दक्षता यह है कि मॉडुलो ऑपरेटर को बिटवाइज़ द्वारा प्रतिस्थापित किया जा सकता है और एक मुखौटा (जैसे x% 1024 == x & 1023) के साथ, और पूर्णांक विभाजन को शिफ्ट राइट ऑपरेशन (जैसे / 1024 / =) से बदला जा सकता है = x / 2 ^ 10 == x >> 10)
vicatcu

15

यदि आपके पास एक SOCK_STREAMसॉकेट है, recvतो स्ट्रीम से बस "पहले 3000 बाइट्स तक" मिलता है। बफर बनाने के लिए कितना बड़ा है, इस पर कोई स्पष्ट मार्गदर्शन नहीं है: केवल एक बार जब आप जानते हैं कि स्ट्रीम कितनी बड़ी है, जब यह सब किया जाता है ;-)।

यदि आपके पास एक SOCK_DGRAMसॉकेट है, और डेटाग्राम बफर से बड़ा है, recvतो बफर को डेटाग्राम के पहले भाग से भरता है, -1 देता है, और EMSGSIZE को ग़लती से सेट करता है। दुर्भाग्य से, यदि प्रोटोकॉल यूडीपी है, तो इसका मतलब है कि बाकी का डेटाग्राम खो गया है - क्यों यूडीपी को एक अविश्वसनीय प्रोटोकॉल कहा जाता है (मुझे पता है कि विश्वसनीय डेटाग्राम प्रोटोकॉल हैं लेकिन वे बहुत लोकप्रिय नहीं हैं - मैं नहीं कर सका टीसीपी / आईपी परिवार में एक का नाम, बाद में बहुत अच्छी तरह से जानने के बावजूद ;-)।

एक बफर को गतिशील रूप से विकसित करने के लिए, इसे शुरू में आवंटित mallocकरें reallocऔर आवश्यकतानुसार उपयोग करें । लेकिन इससे आपको recvUDP स्रोत, अफसोस के साथ मदद नहीं मिलेगी ।


7
चूंकि यूडीपी हमेशा एक ही यूडीपी पैकेट पर लौटता है (भले ही कई सॉकेट बफर में हों) और कोई यूडीपी पैकेट 64 केबी से ऊपर नहीं हो सकता (एक आईपी पैकेट 64 केबी पर भी हो सकता है, खंडित होने पर भी), 64 केबी बफर का उपयोग करके पूरी तरह से सुरक्षित है और गारंटी देता है, कि आप यूडीपी सॉकेट पर किसी रिकर्व के दौरान कभी कोई डेटा नहीं खोते हैं।
मेक्की

7

के लिए SOCK_STREAMसॉकेट, बफर आकार क्योंकि आप बस इंतजार कर बाइट्स के कुछ खींच रहे हैं और आप एक अगली कॉल में और अधिक प्राप्त कर सकते हैं नहीं है, वास्तव में बात। बस जो भी बफर साइज आप उठा सकते हैं उसे उठाएं।

के लिए SOCK_DGRAMसॉकेट, जब तक आप प्रतीक्षा संदेश की फिटिंग हिस्सा मिल जाएगा और बाकी को छोड़ दिया जाएगा। आप निम्न ioctl के साथ प्रतीक्षा डेटाग्राम आकार प्राप्त कर सकते हैं:

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

वैकल्पिक रूप से आप उपयोग कर सकते हैं MSG_PEEKऔर MSG_TRUNCके झंडे recv()कॉल प्रतीक्षा आंकड़ारेख आकार प्राप्त करने के लिए।

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

आपको MSG_PEEKप्रतीक्षा संदेश प्राप्त करने (प्राप्त करने) की आवश्यकता नहीं है - वास्तविक वापस लौटाता है, न कि छोटा आकार; और आपको MSG_TRUNCअपने वर्तमान बफ़र को ओवरफ्लो करने की आवश्यकता नहीं है।

तो फिर तुम सिर्फ malloc(size)असली बफर और recv()डेटाग्राम कर सकते हैं ।


MSG_PEEK | MSG_TRUNC का कोई मतलब नहीं है।
लोर्ने की

3
आप MSG_PEEK को प्रतीक्षा करना चाहते हैं (प्राप्त नहीं) प्रतीक्षा संदेश, इसके आकार को प्राप्त करने के लिए (recv वास्तविक लौटाता है, न कि आकार छोटा) और आपको अपने वर्तमान बफ़र को ओवरफ़्लो न करने के लिए MSG_TRUNC की आवश्यकता है। एक बार जब आप आकार प्राप्त कर लेते हैं तो आप सही बफर आवंटित करते हैं और प्रतीक्षा संदेश प्राप्त नहीं करते हैं।
धूम्रपान जनक

@ एलेक्स मार्टेली का कहना है कि 64 केबी एक यूडीपी पैकेट का अधिकतम आकार है इसलिए यदि हम malloc()64 केबी के बफर के लिए है तो MSG_TRUNCअनावश्यक है?
mLstudent33

1
आईपी ​​प्रोटोकॉल विखंडन का समर्थन करता है, इसलिए डेटाग्राम एक पैकेट से बड़ा हो सकता है - यह कई पैकेटों में खंडित और प्रसारित होगा। इसके अलावा SOCK_DGRAMकेवल यूडीपी नहीं है।
स्मोकू

1

आपके प्रश्न का कोई सटीक उत्तर नहीं है, क्योंकि प्रौद्योगिकी हमेशा कार्यान्वयन-विशिष्ट होने के लिए बाध्य है। मैं मान रहा हूं कि आप यूडीपी में संचार कर रहे हैं क्योंकि आने वाले बफर आकार टीसीपी संचार में समस्या नहीं लाते हैं।

RFC 768 के अनुसार , UDP के लिए पैकेट आकार (हेडर-इनक्लूसिव) 8 से 65 515 बाइट्स तक हो सकते हैं। तो आने वाले बफर के लिए फेल-प्रूफ आकार 65 507 बाइट्स (~ 64KB) है

हालांकि, सभी बड़े पैकेटों को नेटवर्क उपकरणों द्वारा ठीक से रूट नहीं किया जा सकता है, अधिक जानकारी के लिए मौजूदा चर्चा का संदर्भ लें:

अधिकतम थ्रूपुट के लिए एक यूडीपी पैकेट का इष्टतम आकार क्या है?
इंटरनेट पर सबसे बड़ा सुरक्षित यूडीपी पैकेट आकार क्या है


-4

16kb सही है; यदि आप गीगाबिट ईथरनेट का उपयोग कर रहे हैं, तो प्रत्येक पैकेट आकार में 9kb हो सकता है।


3
टीसीपी सॉकेट्स स्ट्रीम हैं, इसका मतलब है कि एक recv कई पैकेटों से संचित डेटा वापस कर सकता है, इसलिए पैकेट का आकार टीसीपी के लिए पूरी तरह अप्रासंगिक है। यूडीपी के मामले में, प्रत्येक आरईवी कॉल अधिकांश एकल यूडीपी पैकेट पर रिटर्न करता है, यहां पैकेट का आकार प्रासंगिक है लेकिन सही पैकेट का आकार लगभग 64 KB है, यदि आवश्यक हो तो एक यूडीपी पैकेट (और अक्सर होगा) खंडित हो जाएगा। हालाँकि, कोई भी IP पैकेट 64 KB से ऊपर नहीं हो सकता है, विखंडन के साथ भी नहीं, इस प्रकार एक UDP सॉकेट पर पुनरावृत्ति हो सकती है, जो कि अधिकतम 64 KB पर है (और जो नहीं लौटाया गया है, वह वर्तमान पैकेट के लिए छोड़ दिया गया है!)
Mecki
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.