সকেট লাইব্রেরিতে আরসিভি কল করার সময় আমার আরসিভি বাফারটি কত বড় হওয়া উচিত


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: প্রশ্নটি প্রকৃতপক্ষে যথাযথভাবে বোঝায় না কারণ স্ট্রিম সকেটে প্যাকেটের ধারণা নেই - এগুলি কেবল বাইটের একটি ধারাবাহিক স্ট্রিম। আপনার বাফারের জন্য জায়গা থাকার চেয়ে যদি আরও বেশি বাইটস পড়ার জন্য উপলব্ধ থাকে তবে সেগুলি ওএস দ্বারা সারিবদ্ধ থাকবে এবং আপনার পরবর্তী কলটিতে উপলভ্য হবে recv

  • SOCK_DGRAM: অতিরিক্ত বাইটগুলি ফেলে দেওয়া হয়।

আমি কীভাবে জানতে পারি যে আমি পুরো বার্তাটি পেয়েছি?

  • SOCK_STREAM: আপনার অ্যাপ্লিকেশন-স্তরের প্রোটোকলটিতে বার্তাটির শেষ-নির্ধারণ করার জন্য আপনাকে কিছু উপায় তৈরি করতে হবে। সাধারণত এটি হয় দৈর্ঘ্যের উপসর্গ (বার্তার দৈর্ঘ্যের সাথে প্রতিটি বার্তা শুরু করা) বা একটি বার্তা প্রান্তিক (যা উদাহরণস্বরূপ কোনও পাঠ্য-ভিত্তিক প্রোটোকলে একটি নতুন লাইন হতে পারে)। তৃতীয়, কম-ব্যবহৃত, বিকল্পটি প্রতিটি বার্তার জন্য একটি নির্দিষ্ট আকার ম্যান্ডেট করা। এই বিকল্পগুলির সংমিশ্রণগুলিও সম্ভব - উদাহরণস্বরূপ, একটি স্থির-আকারের শিরোনাম যাতে দৈর্ঘ্যের মান অন্তর্ভুক্ত থাকে।

  • SOCK_DGRAM: একটি একক recvকল সর্বদা একটি একক ডেটাগ্রাম দেয়।

কোনও উপায় আছে যে আমি কোনও বাফারকে স্থির পরিমাণের নির্দিষ্ট পরিমাণ না তৈরি করতে পারি, যাতে আমি স্থানটি দৌড়ে যাওয়ার ভয় ছাড়াই এটিতে যুক্ত করতে পারি?

না। তবে, আপনি ব্যবহার করে বাফারকে পুনরায় আকার দেওয়ার চেষ্টা করতে পারেন realloc()(যদি এটি মূলত বরাদ্দ করা হয় malloc()বা calloc()এটি হ'ল)।


1
আমি যে প্রোটোকলটি ব্যবহার করছি তার বার্তার শেষে আমার একটি "/ r / n / r / n" আছে have এবং আমার কিছুক্ষণ লুপ আছে, ভিতরে আমি পুনরায় কল করছি আমি recv_buffer এর শুরুতে বার্তাটি রাখি। এবং আমার বিবৃতিটি ((! (স্টারস্টার (recv_buffer, "\ r \ n \ r \ n"))) এর মতো দেখায়; আমার প্রশ্ন হ'ল, কোনও পুনর্বিবেচনার পক্ষে "\ r" n "পাওয়া এবং পরবর্তী recv "\ r \ n" পান, যাতে আমার সময়কালের অবস্থাটি কখনই সত্য হয় না?
সময়কালের

3
হ্যাঁ, তাই আপনি যদি সম্পূর্ণ বার্তা না পেয়ে এবং recvআংশিক বার্তাটি অনুসরণ করে পরবর্তী থেকে বাফারে পরবর্তীটি থেকে বাইটগুলি স্টাফ করেন তবে আপনি চারপাশে লুপিং করে সমস্যার সমাধান করতে পারেন । আপনি ব্যবহার করা উচিত নয় strstr()কাঁচা দ্বারা ভর্ত্তি বাফার উপর recv()- কোন গ্যারান্টি এটি তাই এটি কারণ হতে পারে, একটি nul-টারমিনেটর রয়েছে সেখানে strstr()ক্র্যাশ করতে পারে।
ক্যাফে 20'10

3
ইউডিপির ক্ষেত্রে, 1400 বাইটের উপরে ইউডিপি প্যাকেটগুলি প্রেরণে কোনও ভুল নেই। বিভাজন পুরোপুরি আইনী এবং আইপি প্রোটোকলের একটি মৌলিক অংশ (এমনকি আইপিভি 6-তেও রয়েছে, তবে সর্বদা প্রাথমিক প্রেরককে অবশ্যই খণ্ডন সম্পাদন করতে হবে)। ইউডিপি-র জন্য আপনি সর্বদা সংরক্ষণ করুন আপনি যদি 64 কেবি বাফার ব্যবহার করেন তবে কোনও আইপি প্যাকেট (ভি 4 বা ভি 6) আকারে 64 কেবি এর বেশি হতে পারে না (এমনকি খণ্ডিত হওয়ার পরেও নয়) এবং এটিতে শিরোনাম আইআইআরসি অন্তর্ভুক্ত থাকে, তাই ডেটা সর্বদা থাকবে নিশ্চিত হিসাবে 64 কেবি নীচে।
মক্কি

1
@ কেএফ-কে কি পুনরায় () পুনরুদ্ধার করতে প্রতিটি কলে বাফার খালি করা দরকার? আমি কোড লুপ দেখেছি এবং ডেটা সংগ্রহ করেছি এবং এটি আবার লুপ করব যাতে আরও ডেটা সংগ্রহ করা উচিত। বাফারটি যদি কখনও পূর্ণ হয় তবে আপনার বাফারের জন্য বরাদ্দকৃত মেমরির পরিমাণ পাস করে লেখার কারণে কোনও স্মৃতি লঙ্ঘন এড়াতে আপনার খালি করার দরকার নেই?
অ্যালেক্স_নাবু

1
@ অ্যালেক্স_নাবু: যতক্ষণ না সেখানে কিছু জায়গা বাকী থাকে ততক্ষণ আপনাকে এটিকে খালি করার দরকার নেই, এবং recv()জায়গা বাকী থাকার চেয়ে বেশি বাইট লেখার কথা বলবেন না ।
ক্যাফে

16

টিসিপি-র মতো প্রোটোকলগুলির জন্য, আপনি আপনার বাফারকে যে কোনও আকারে সেট করতে পারেন। এটি বলেছে যে সাধারণ মানগুলি যেমন 29 এর 4096 বা 8192 এর মতো শক্তি হিসাবে সুপারিশ করা হয়।

যদি আরও ডেটা থাকে তবে আপনার বাফার কী, এটি আপনার পরবর্তী কলের জন্য কেবল কার্নেলে সংরক্ষণ করা হবে recv

হ্যাঁ, আপনি আপনার বাফারটি বাড়িয়ে রাখতে পারেন। আপনি অফসেটে শুরু করে বাফারের মাঝখানে একটি পুনঃস্থাপন করতে পারেন idx, আপনি এটি করবেন:

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

6
দুজনের শক্তি একাধিক উপায়ে আরও দক্ষ হতে পারে, এবং দৃ strongly়ভাবে প্রস্তাবিত।
ইয়ান রামিন

3
@ থিয়েটারে বিশদভাবে উল্লেখযোগ্য দক্ষতা হ'ল মডুলো অপারেটরটি বিটওয়াইজ করে এবং একটি মাস্ক (উদাহরণস্বরূপ x% 1024 == x & 1023) দ্বারা প্রতিস্থাপন করা যেতে পারে, এবং পূর্ণসংখ্যা বিভাগ একটি শিফট রাইট অপারেশন দ্বারা প্রতিস্থাপিত হতে পারে (যেমন x / 1024 = = x / 2 ^ 10 == x >> 10)
ভিসাতচু

15

আপনার যদি SOCK_STREAMসকেট থাকে তবে recvকেবল স্ট্রিম থেকে "প্রথম 3000 বাইট অবধি" পান। বাফারটি কীভাবে তৈরি করা যায় সে সম্পর্কে কোনও পরিষ্কার নির্দেশিকা নেই: কেবলমাত্র যখন আপনি জানেন যে একটি স্ট্রিমটি কতটা বড়, যখন এটি শেষ হয়ে যায় ;-)।

আপনার যদি SOCK_DGRAMসকেট থাকে এবং ডেটাগ্রামটি বাফারের চেয়ে বড় recvহয়, ডেটাগ্রামের প্রথম অংশটি দিয়ে বাফারটি পূরণ করে, -1 প্রদান করে এবং ইএমএসজিএসআইজেই এর্ন সেট করে। দুর্ভাগ্যক্রমে, প্রোটোকলটি যদি ইউডিপি হয়, এর অর্থ বাকি বাকী ডেটাগ্রামটি হারিয়ে গেছে - ইউডিপিকে কেন অবিশ্বাস্য বলা হয় তার একটি অংশ প্রোটোকল (আমি জানি যে নির্ভরযোগ্য ডেটাগ্রাম প্রোটোকল রয়েছে তবে সেগুলি খুব জনপ্রিয় নয় - আমি পারিনি টিসিপি / আইপি পরিবারে একটির নাম রাখুন, বেশিরভাগটি ভালভাবে জানা থাকলেও ;-)।

গতিশীলভাবে একটি বাফার বাড়ানোর জন্য, এটি প্রাথমিকভাবে বরাদ্দ করুন mallocএবং reallocপ্রয়োজনমতো ব্যবহার করুন । তবে এটি আপনাকে recvইউডিপি উত্স থেকে সহায়তা করবে না হায়!


7
যেহেতু ইউডিপি সর্বদা একটি ইউডিপি প্যাকেটে ফিরে আসে (একাধিক সকেট বাফারে থাকলেও) এবং কোনও ইউডিপি প্যাকেট KB৪ কেবি এর উপরে হতে পারে না (একটি আইপি প্যাকেট সর্বাধিক KB৪ কেবি হতে পারে এমনকি খণ্ডিত হয়েও), একটি a৪ কেবি বাফার ব্যবহার করে একেবারে সুরক্ষিত এবং গ্যারান্টিযুক্ত, আপনি কোনও ইউডিপি সকেটের পুনর্নির্মাণের সময় কোনও ডেটা কখনই হারাবেন না।
মক্কি

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অপেক্ষা করার বার্তাটি উঁকি দিতে হবে (গ্রহণ করা হবে না) - recv আসলটি, কাটা আকার নয়; এবং আপনার MSG_TRUNCআপনার বর্তমান বাফারটি উপচে পড়তে হবে না।

তারপরে আপনি কেবল malloc(size)আসল বাফার এবং recv()ডেটাগ্রাম করতে পারেন ।


MSG_PEEK | MSG_TRUNC কোনও অর্থ দেয় না।
মারকুইস

3
আপনি চাইছেন যে MSG_PEEK অপেক্ষার বার্তাটি উঁকি দেওয়া (গ্রহণ না করা), তার আকার পেতে (recv প্রকৃত, কাটানো আকারটি দেয় না) এবং আপনার বর্তমান বাফারটি উপচে না ফেলার জন্য আপনার MSG_TRUNC দরকার। আকারটি পেয়ে গেলে আপনি সঠিক বাফার বরাদ্দ করেন এবং অপেক্ষার বার্তাটি (উঁকি দেন না, কাটবেন না) পান।
স্মোক্কু

@ অ্যালেক্স মার্তেল্লি বলেছেন B৪ কেবি ইউডিপি প্যাকেটের সর্বোচ্চ আকার তাই যদি আমরা K৪ কেবি malloc()বাফারের MSG_TRUNCজন্য অপ্রয়োজনীয়?
mLstudent33

1
আইপি প্রোটোকল টুকরো টুকরো সমর্থন করে, তাই ডেটাগ্রাম একক প্যাকেটের চেয়ে বড় হতে পারে - এটি খণ্ডিত এবং একাধিক প্যাকেটে সংক্রমণিত হবে। এছাড়াও SOCK_DGRAMকেবল ইউডিপি নয়।
স্মোক্কু

1

আপনার প্রশ্নের কোনও নিখুঁত উত্তর নেই, কারণ প্রযুক্তি সর্বদা বাস্তবায়ন-নির্দিষ্ট হতে বাধ্য। আমি ধরে নিচ্ছি আপনি ইউডিপিতে যোগাযোগ করছেন কারণ আগত বাফার আকার টিসিপি যোগাযোগে সমস্যা আনবে না।

আরএফসি 768 অনুসারে , ইউডিপির জন্য প্যাকেটের আকার (শিরোলেখ-সমেত) 8 থেকে 6555 বাইট পর্যন্ত হতে পারে। সুতরাং আগত বাফারের জন্য ব্যর্থ-প্রমাণের আকার হ'ল 65 507 বাইট (~ 64KB)

তবে, সমস্ত বড় প্যাকেটগুলি সঠিকভাবে নেটওয়ার্ক ডিভাইসগুলি দ্বারা চালিত করা যায় না, আরও তথ্যের জন্য বিদ্যমান আলোচনার উল্লেখ করুন:

সর্বাধিক থ্রুপুট জন্য কোন ইউডিপি প্যাকেটের অনুকূল আকারটি কী?
ইন্টারনেটে বৃহত্তম নিরাপদ ইউডিপি প্যাকেটের আকারটি কী


-4

16 কেবি প্রায় সঠিক; আপনি যদি গিগাবিট ইথারনেট ব্যবহার করেন তবে প্রতিটি প্যাকেট 9kb আকারের হতে পারে।


3
টিসিপি সকেটগুলি হ'ল স্ট্রিম, এর অর্থ কোনও পুনর্নির্মাণ একাধিক প্যাকেট থেকে সংগৃহীত ডেটা ফেরত দিতে পারে, তাই প্যাকেটের আকার টিসিপি-র জন্য সম্পূর্ণ অপ্রাসঙ্গিক। ইউডিপি-র ক্ষেত্রে, প্রতিটি আরসিভি কল সর্বাধিক একক ইউডিপি প্যাকেটে প্রত্যাবর্তন করে, এখানে প্যাকেটের আকার প্রাসঙ্গিক তবে সঠিক প্যাকেটের আকার প্রায় 64 কেবি, কারণ ইউডিপি প্যাকেট প্রয়োজন (এবং প্রায়শই) খণ্ডিত হতে পারে। তবে কোনও আইপি প্যাকেট KB৪ কেবি এর উপরে হতে পারে না এমনকি খণ্ডিতকরণের সাথেও নয়, সুতরাং কোনও ইউডিপি সকেটে রিভিভ সর্বাধিক KB৪ কেবি রিটার্ন করতে পারে (এবং যা প্রত্যাবর্তিত হয় না তা বর্তমান প্যাকেটের জন্য বাতিল করা হয়!)
মেকি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.