স্কেলযোগ্য টিসিপি / আইপি ভিত্তিক সার্ভার কীভাবে লিখবেন


148

আমি একটি নতুন উইন্ডোজ পরিষেবা অ্যাপ্লিকেশন লেখার নকশার পর্যায়ে আছি যা দীর্ঘকাল ধরে চলমান সংযোগগুলির জন্য টিসিপি / আইপি সংযোগ গ্রহণ করে (যেমন এটি এইচটিটিপির মতো নয় যেখানে অনেক সংক্ষিপ্ত সংযোগ রয়েছে, বরং ক্লায়েন্ট সংযোগ স্থাপন করে এবং ঘন্টা বা দিন সংযুক্ত থাকে বা এমনকি সপ্তাহ)।

আমি নেটওয়ার্ক আর্কিটেকচারটি ডিজাইনের সর্বোত্তম উপায়ে আইডিয়া সন্ধান করছি। আমি পরিষেবাটির জন্য কমপক্ষে একটি থ্রেড শুরু করতে চাই। আমি অ্যাসিঞ্চ এপিআই (বিগিনিক্রিভি, ইত্যাদি।) ব্যবহার করার বিষয়ে বিবেচনা করছি যেহেতু আমি জানি না যে আমি কোনও নির্দিষ্ট সময়ে (সম্ভবত কয়েকশ) কত সংখ্যক ক্লায়েন্ট সংযুক্ত হয়েছি। আমি অবশ্যই প্রতিটি সংযোগের জন্য কোনও থ্রেড শুরু করতে চাই না।

ডেটা প্রাথমিকভাবে আমার সার্ভার থেকে ক্লায়েন্টদের কাছে প্রবাহিত হবে, তবে উপলক্ষ্যে ক্লায়েন্টদের কাছ থেকে কিছু কমান্ড প্রেরণ করা হবে। এটি মূলত একটি মনিটরিং অ্যাপ্লায়টোন যা আমার সার্ভারটি পর্যায়ক্রমে ক্লায়েন্টদের কাছে ডেটা প্রেরণ করে।

এটিকে যতটা সম্ভব স্কেলযোগ্য করার সর্বোত্তম উপায়ে কোনও পরামর্শ? বেসিক ওয়ার্কফ্লো? ধন্যবাদ।

সম্পাদনা: পরিষ্কার হওয়ার জন্য, আমি। নেট ভিত্তিক সমাধানগুলি সন্ধান করছি (সম্ভব হলে সি #, তবে কোনও নেট ভাষা কাজ করবে)

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

সম্পাদনা: আমি প্রত্যেককে ধন্যবাদ জানাতে চাই যারা ভাল উত্তর দিয়েছে। দুর্ভাগ্যক্রমে, আমি কেবল একটি গ্রহণ করতে পারি, এবং আমি আরও পরিচিত বিগ / সমাপ্ত পদ্ধতিটি গ্রহণ করতে বেছে নিয়েছি। এসাকের সমাধানটি আরও ভাল হতে পারে তবে এটি এখনও যথেষ্ট নতুন যে এটি কীভাবে কার্যকর হবে তা আমি নিশ্চিতভাবে জানি না।

আমি যে উত্তরগুলি ভাল বলে মনে করেছি সেগুলি উত্সাহিত করেছি, আমি আশা করি আপনারা ছেলেদের জন্য আরও কিছু করতে পারলাম। আবার ধন্যবাদ.


1
আপনি কি একেবারে নিশ্চিত যে এটি দীর্ঘকালীন সংযোগ হওয়া দরকার? প্রদত্ত সীমিত তথ্য থেকে জানা শক্ত, তবে কেবল প্রয়োজন যদি আমি তা করতে চাই তবেই ..
মার্কেট

হ্যাঁ, এটি দীর্ঘকাল চলতে হবে। ডেটা অবশ্যই রিয়েল-টাইমে আপডেট হওয়া উচিত, তাই আমি পর্যায়ক্রমিক পোলিং করতে পারি না, ডেটা ক্লায়েন্টের সাথে ধাক্কা খাওয়ার সাথে সাথে এটি ধাক্কা দিতে হবে, যার অর্থ ধ্রুব সংযোগ।
এরিক ফানকেনবাশ

1
এটি কোনও বৈধ কারণ নয়। এইচটিটিপি সমর্থন দীর্ঘ চলমান সংযোগ ঠিক ঠিক। আপনি কেবল একটি সংযোগ খোলেন এবং একটি রিপসনস (স্টল পোল) এর জন্য অপেক্ষা করুন। এটি অনেক AJAX শৈলীর অ্যাপ্লিকেশন ইত্যাদির জন্য দুর্দান্ত কাজ করে
you

2
জিমেইল পর্যায়ক্রমে ইমেলের জন্য পোলিংয়ের মাধ্যমে কাজ করে, এটি দীর্ঘকাল চলমান সংযোগ রাখে না। এটি ইমেলের জন্য ঠিক আছে, যেখানে রিয়েল-টাইম প্রতিক্রিয়া প্রয়োজন হয় না।
এরিক ফানকেনবাশ

2
পোলিং, বা টানুন, ভাল স্কেল করে তবে খুব শীঘ্রই বিকাশ ঘটে। ঠেলাঠেলি পাশাপাশি স্কেল করে না, তবে বিলম্বিতা হ্রাস করতে বা দূর করতে সহায়তা করে।
অ্যান্ড্রুবাদার

উত্তর:


92

আমি এর আগেও এরকম কিছু লিখেছি। আমার গবেষণা থেকে কয়েক বছর আগে দেখানো হয়েছিল যে আপনার নিজের সকেট প্রয়োগটি অসিঙ্ক্রোনাস সকেট ব্যবহার করে সেরা বেট ছিল। এর অর্থ হ'ল ক্লায়েন্টরা আসলে কিছু না করে আসলে অপেক্ষাকৃত কম সংস্থান প্রয়োজন। যা ঘটে থাকে তা। নেট থ্রেড পুল দ্বারা পরিচালিত হয়।

আমি এটি ক্লাস হিসাবে লিখেছি যা সার্ভারগুলির জন্য সমস্ত সংযোগ পরিচালনা করে।

সমস্ত ক্লায়েন্ট সংযোগগুলি ধরে রাখতে আমি কেবল একটি তালিকা ব্যবহার করেছি, তবে বৃহত্তর তালিকার জন্য আপনার যদি দ্রুত অনুসন্ধানের প্রয়োজন হয় তবে আপনি এটি চাইলে লিখতে পারেন।

private List<xConnection> _sockets;

এছাড়াও অকেজো সংযোগের জন্য আপনার অবশ্যই সকেট শোনা দরকার।

private System.Net.Sockets.Socket _serverSocket;

প্রারম্ভিক পদ্ধতিটি আসলে সার্ভার সকেট শুরু করে এবং কোনও অযোগ্য সংযোগের জন্য শোনার শুরু করে।

public bool Start()
{
  System.Net.IPHostEntry localhost = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
  System.Net.IPEndPoint serverEndPoint;
  try
  {
     serverEndPoint = new System.Net.IPEndPoint(localhost.AddressList[0], _port);
  }
  catch (System.ArgumentOutOfRangeException e)
  {
    throw new ArgumentOutOfRangeException("Port number entered would seem to be invalid, should be between 1024 and 65000", e);
  }
  try
  {
    _serverSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
   }
   catch (System.Net.Sockets.SocketException e)
   {
      throw new ApplicationException("Could not create socket, check to make sure not duplicating port", e);
    }
    try
    {
      _serverSocket.Bind(serverEndPoint);
      _serverSocket.Listen(_backlog);
    }
    catch (Exception e)
    {
       throw new ApplicationException("Error occured while binding socket, check inner exception", e);
    }
    try
    {
       //warning, only call this once, this is a bug in .net 2.0 that breaks if 
       // you're running multiple asynch accepts, this bug may be fixed, but
       // it was a major pain in the ass previously, so make sure there is only one
       //BeginAccept running
       _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
    }
    catch (Exception e)
    {
       throw new ApplicationException("Error occured starting listeners, check inner exception", e);
    }
    return true;
 }

আমি কেবল ব্যতিক্রম হ্যান্ডলিং কোডটি দেখতে খারাপ মনে করব, তবে এর কারণটি হ'ল সেখানে আমার ব্যতিক্রম দমন কোডটি ছিল যাতে কোনও ব্যতিক্রম দমন করা যায় এবং falseযদি কোনও কনফিগারেশন বিকল্প সেট করা থাকে তবে ফিরে আসতে পারি, তবে আমি এটিকে অপসারণ করতে চেয়েছিলাম বংশবৃদ্ধির জন্য

_ServerSocket.BeginAcসেপ্ট (নতুন AsyncCallback (গ্রহণ কলাপব্যাক)), _serverSket) মূলত আমাদের ব্যবহারকারী যখনই কোনও সংযোগ যুক্ত হয় তখন গ্রহণযোগ্য কলব্যাক পদ্ধতিতে কল করতে আমাদের সার্ভার সকেট সেট করে। এই পদ্ধতিটি। নেট থ্রেডপুল থেকে চালিত হয়, যা যদি আপনার অনেকগুলি ব্লকিং ক্রিয়াকলাপ থাকে তবে স্বয়ংক্রিয়ভাবে অতিরিক্ত কর্মী থ্রেড তৈরি করতে পরিচালনা করে। এটি সার্ভারে কোনও লোডকে সর্বোত্তমভাবে পরিচালনা করতে হবে।

    private void acceptCallback(IAsyncResult result)
    {
       xConnection conn = new xConnection();
       try
       {
         //Finish accepting the connection
         System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
         conn = new xConnection();
         conn.socket = s.EndAccept(result);
         conn.buffer = new byte[_bufferSize];
         lock (_sockets)
         {
           _sockets.Add(conn);
         }
         //Queue recieving of data from the connection
         conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
         //Queue the accept of the next incomming connection
         _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
       }
       catch (SocketException e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
         //Queue the next accept, think this should be here, stop attacks based on killing the waiting listeners
         _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
       }
       catch (Exception e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
         //Queue the next accept, think this should be here, stop attacks based on killing the waiting listeners
         _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);
       }
     }

উপরের কোডটি মূলত সবেমাত্র সংযোগটি এসেছিল তা গ্রহণ করেই শেষ করে, সারিগুলি BeginReceiveযা একটি কলব্যাক যা ক্লায়েন্ট ডেটা প্রেরণ করার সময় চলবে এবং তারপরে পরবর্তী সারিতে acceptCallbackযেটি পরবর্তী ক্লায়েন্ট সংযোগটি গ্রহণ করবে তা গ্রহণ করবে।

BeginReceiveপদ্ধতি কল কি সকেট কি যখন এটি ক্লায়েন্ট থেকে তথ্য পায় না বলে নয়। এর জন্য BeginReceive, আপনাকে এটিকে একটি বাইট অ্যারে দেওয়া দরকার, এটি ক্লায়েন্ট যখন ডেটা প্রেরণ করবে তখন এটি ডেটা অনুলিপি করবে। ReceiveCallbackপদ্ধতি বলা হবে, যা আমরা গ্রহণ ডেটা কিভাবে হ্যান্ডেল।

private void ReceiveCallback(IAsyncResult result)
{
  //get our connection from the callback
  xConnection conn = (xConnection)result.AsyncState;
  //catch any errors, we'd better not have any
  try
  {
    //Grab our buffer and count the number of bytes receives
    int bytesRead = conn.socket.EndReceive(result);
    //make sure we've read something, if we haven't it supposadly means that the client disconnected
    if (bytesRead > 0)
    {
      //put whatever you want to do when you receive data here

      //Queue the next receive
      conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
     }
     else
     {
       //Callback run but no data, close the connection
       //supposadly means a disconnect
       //and we still have to close the socket, even though we throw the event later
       conn.socket.Close();
       lock (_sockets)
       {
         _sockets.Remove(conn);
       }
     }
   }
   catch (SocketException e)
   {
     //Something went terribly wrong
     //which shouldn't have happened
     if (conn.socket != null)
     {
       conn.socket.Close();
       lock (_sockets)
       {
         _sockets.Remove(conn);
       }
     }
   }
 }

সম্পাদনা: এই প্যাটার্নে আমি কোডের এই ক্ষেত্রে এটি উল্লেখ করতে ভুলে গেছি:

//put whatever you want to do when you receive data here

//Queue the next receive
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);

আমি যা করবো তা হ'ল আপনি যা যা কোড চান তা হ'ল বার্তাগুলিতে প্যাকেটগুলি পুনরায় অপসারণ করা এবং তারপরে থ্রেড পুলে কাজ হিসাবে তাদের তৈরি করা। এই বার্তাটি প্রক্রিয়াকরণ কোড চলমান থাকাকালীন ক্লায়েন্টের কাছ থেকে পরবর্তী ব্লকের বিগিনি রিসিভ বিলম্বিত হয় না।

স্বীকৃতি কলব্যাক এন্ড রিসিভ কল করে ডেটা সকেট পড়া শেষ করে। এটি প্রারম্ভিক প্রাপ্ত কার্যটি সরবরাহ করে বাফারটি পূরণ করে। একবার আপনি যেখানে মন্তব্যটি ছেড়ে গেছেন সেখানে যা যা করতে চাইলে আমরা পরবর্তী BeginReceiveপদ্ধতিতে কল করি যা ক্লায়েন্ট আরও কোনও ডেটা প্রেরণ করলে কলব্যাকটি আবার চালাবে। এখন এখানে সত্যই জটিল অংশটি রয়েছে, যখন ক্লায়েন্টটি ডেটা প্রেরণ করে, আপনার প্রাপ্ত কলব্যাকটি কেবলমাত্র বার্তার অংশের সাথে কল করা যেতে পারে। পুনরায় বিহীনতা খুব জটিল হয়ে উঠতে পারে। আমি আমার নিজস্ব পদ্ধতি ব্যবহার করেছি এবং এটি করার জন্য এক ধরণের মালিকানাধীন প্রোটোকল তৈরি করেছি। আমি এটিকে ছেড়ে দিয়েছি, তবে আপনি যদি অনুরোধ করেন তবে আমি এটিকে যুক্ত করতে পারি This এই হ্যান্ডলারটি আসলে আমার লেখা কোডের সবচেয়ে জটিল অংশ ছিল।

public bool Send(byte[] message, xConnection conn)
{
  if (conn != null && conn.socket.Connected)
  {
    lock (conn.socket)
    {
    //we use a blocking mode send, no async on the outgoing
    //since this is primarily a multithreaded application, shouldn't cause problems to send in blocking mode
       conn.socket.Send(bytes, bytes.Length, SocketFlags.None);
     }
   }
   else
     return false;
   return true;
 }

উপরের প্রেরণ পদ্ধতিটি আসলে একটি সিঙ্ক্রোনাস Sendকল ব্যবহার করে , আমার জন্য বার্তা আকারের এবং আমার অ্যাপ্লিকেশনটির বহুবিশ্লেষিত প্রকৃতির কারণে এটি ঠিক ছিল। আপনি যদি প্রতিটি ক্লায়েন্টকে প্রেরণ করতে চান তবে আপনাকে কেবলমাত্র_সকেট তালিকার মাধ্যমে লুপ করতে হবে।

উপরোক্ত রেফারেন্সের জন্য আপনি যে x সংযোগ ক্লাসটি দেখেছেন সেটি হ'ল বকেয়া বাফার অন্তর্ভুক্ত করার জন্য সকেটের জন্য একটি সাধারণ মোড়ক এবং আমার প্রয়োগে কিছু অতিরিক্ত।

public class xConnection : xBase
{
  public byte[] buffer;
  public System.Net.Sockets.Socket socket;
}

রেফারেন্সের জন্য এখানে usingআমি অন্তর্ভুক্ত করা আছে যেহেতু আমি অন্তর্ভুক্ত না থাকায় আমি সর্বদা বিরক্ত হই।

using System.Net.Sockets;

আমি আশা করি এটি সহায়ক, এটি সবচেয়ে পরিষ্কার কোড নাও হতে পারে, তবে এটি কার্যকর হয়। কোডটির কয়েকটি সূক্ষ্মতা রয়েছে যা আপনার পরিবর্তনের বিষয়ে উদাসীন হওয়া উচিত। একটির জন্য, BeginAcceptযে কোনও সময়ে কেবলমাত্র একজনকে কল করা উচিত। এটির চারপাশে খুব বিরক্তিকর। নেট বাগ ছিল যা বহু বছর আগে ছিল তাই আমি বিশদটি মনে করি না।

এছাড়াও ReceiveCallbackকোডটিতে আমরা পরবর্তী প্রাপ্তির সারিবদ্ধ হওয়ার আগে সকেট থেকে প্রাপ্ত কিছু প্রক্রিয়া করি। এর অর্থ হ'ল একক সকেটের জন্য আমরা কেবল ReceiveCallbackসময়ে সময়ে যে কোনও সময়ে এসেছি এবং থ্রেড সিঙ্ক্রোনাইজেশন ব্যবহার করার দরকার নেই। যাইহোক, আপনি যদি ডেটাটি টান দেওয়ার সাথে সাথে পরের প্রাপ্তিকে কল করার জন্য এটি পুনরায় অর্ডার করেন, যা কিছুটা দ্রুত হতে পারে, আপনাকে থ্রেডগুলি সঠিকভাবে সিঙ্ক্রোনাইজ করতে হবে তা নিশ্চিত করতে হবে।

এছাড়াও, আমি আমার কোডটি প্রচুর হ্যাক করে দিয়েছি, তবে যা ঘটছে তার সারাংশ রেখেছি। আপনার ডিজাইনের জন্য এটি একটি ভাল শুরু হওয়া উচিত। এই সম্পর্কে আপনার যদি আরও কোনও প্রশ্ন থাকে তবে একটি মন্তব্য করুন।


1
এটি একটি উত্তম উত্তর কেভিন .. দেখে মনে হচ্ছে আপনি অনুগ্রহ পাওয়ার জন্য ট্র্যাকে রয়েছেন। :)
এরিক ফানকেনবাশ

6
আমি জানি না কেন এটি সবচেয়ে বেশি ভোট দেওয়া উত্তর। সিগন্যাল * শেষ * নেটওয়ার্কিং করার জন্য সি # তে দ্রুততম উপায় নয় বা সর্বাধিক অতি স্কেলেবলও নয়। এটি সিঙ্ক্রোনাসের চেয়ে দ্রুত, তবে উইন্ডোজের হুডের নিচে প্রচুর অপারেশন রয়েছে যা এই নেটওয়ার্কটির পথটি সত্যই ধীর করে দেয়।
এসাক

6
পূর্বের মন্তব্যে এসাক কী লিখেছেন তা মাথায় রাখুন। প্রারম্ভ-শেষের প্যাটার্নটি সম্ভবত আপনার জন্য একটি পয়েন্ট অবধি কাজ করবে, হেক আমার কোড বর্তমানে শুরুর দিকে ব্যবহার করছে, তবে। নেট 3.5 এ এর ​​সীমাবদ্ধতার উন্নতি রয়েছে। আমি অনুগ্রহ সম্পর্কে চিন্তা করি না তবে আপনি এই পদ্ধতির বাস্তবায়ন করলেও আপনি আমার উত্তরে লিঙ্কটি পড়তে সুপারিশ করবেন। "সংস্করণ 3.5 এ সকেট পারফরম্যান্স বর্ধন"
jvenderh

1
আমি কেবল তাদের মধ্যে ফেলে দিতে চেয়েছিলাম যেহেতু আমি সম্ভবত যথেষ্ট পরিষ্কার ছিলাম না, এটি .NET 2.0 যুগের কোড যেখানে আমি বেঁকেছি এটি একটি খুব কার্যকর প্যাটার্ন। যাইহোক, এসাকের উত্তরটি কিছুটা আরও আধুনিক বলে মনে হচ্ছে যদি নেট .৩.৫ লক্ষ্য করে তবে আমার কাছে একমাত্র নিটপিক ঘটনাগুলি নিক্ষেপ করা হয় :) তবে এটি সহজেই পরিবর্তন করা যেতে পারে। এছাড়াও, আমি এই কোডটির মাধ্যমে থ্রুটপুট পরীক্ষা করেছি এবং ডুয়াল কোর ওপ্টারন 2 গিগাহার্জ 100 এমবিপিএস ইথারনেট সর্বাধিক আউট করতে সক্ষম হয়েছি এবং এটি এই কোডের উপরে একটি এনক্রিপশন স্তর যুক্ত করেছে।
কেভিন নিসবেট

1
@ কেভিননিসবেট আমি জানি এটি বেশ দেরি হয়ে গেছে, তবে যে কেউ এই উত্তরটি নিজের সার্ভারগুলি ডিজাইনের জন্য ব্যবহার করছে - তাদের পাঠানোও অবিচ্ছিন্ন হওয়া উচিত কারণ অন্যথায় আপনি অচলাবস্থার সম্ভাবনার জন্য নিজেকে উন্মুক্ত করেন। যদি উভয় পক্ষই তাদের নিজ নিজ বাফারগুলিকে পূরণ করে এমন ডেটা লেখেন তবে Sendপদ্ধতিগুলি উভয় পক্ষেই অনির্দিষ্টকালের জন্য অবরুদ্ধ হবে, কারণ ইনপুট ডেটা পড়ার কিছুই নেই।
লুয়ান

83

সি # তে নেটওয়ার্ক অপারেশন করার বিভিন্ন উপায় রয়েছে। এঁরা সকলেই হুডের নীচে পৃথক পৃথক প্রক্রিয়া ব্যবহার করেন এবং এইভাবে উচ্চতর সম্মতিতে বড় পারফরম্যান্স সমস্যার সম্মুখীন হন। বিগেইন * অপারেশনগুলি এর মধ্যে একটি যা অনেকেই প্রায়শই দ্রুত নেটওয়ার্কিং করার দ্রুত / দ্রুততম উপায় বলে ভুল করে।

এই সমস্যাগুলি সমাধান করার জন্য, তারা পদ্ধতিগুলির * অ্যাসিঙ্ক সেটটি চালু করেছে: এমএসডিএন থেকে http://msdn.microsoft.com/en-us/library/system.net.sockets.sketasynceventargs.aspx

সকেটঅ্যাসেন্সিয়েেন্টআরগস ক্লাসটি সিস্টেম.নেট.সকেটস .. :: ...... সকেট ক্লাস যা একটি বিকল্প অ্যাসিনক্রোনাস প্যাটার্ন সরবরাহ করে যা বিশেষী উচ্চ-সম্পাদনা সকেট অ্যাপ্লিকেশনগুলির সাহায্যে ব্যবহার করা যেতে পারে a এই শ্রেণিটি বিশেষত নেটওয়ার্ক সার্ভার অ্যাপ্লিকেশনগুলির জন্য তৈরি করা হয়েছিল যাতে উচ্চ কার্যকারিতা প্রয়োজন। কোনও অ্যাপ্লিকেশন বর্ধিত অ্যাসিনক্রোনাস প্যাটার্নটি একচেটিয়াভাবে বা কেবলমাত্র লক্ষ্যযুক্ত গরম অঞ্চলে ব্যবহার করতে পারে (উদাহরণস্বরূপ, যখন প্রচুর পরিমাণে ডেটা গ্রহণ করা হয়)।

এই বর্ধনের প্রধান বৈশিষ্ট্য হ'ল উচ্চ-ভলিউম অ্যাসিনক্রোনাস সকেট I / O চলাকালীন পুনরাবৃত্তি বরাদ্দ এবং বস্তুর সমন্বয়কে এড়ানো। বিগ / এন্ড ডিজাইন প্যাটার্ন বর্তমানে সিস্টেম.নেট.সকেটস দ্বারা প্রয়োগ করা হয়েছে .. :: সকেট শ্রেণিতে একটি সিস্টেমের প্রয়োজন .. :: .. প্রতিটি অ্যাসিনক্রোনাস সকেট ক্রিয়াকলাপের জন্য আইএএনসিএনআরসাল্ট অবজেক্ট বরাদ্দ করা উচিত।

কভারগুলির আওতায়, * এসিঙ্ক এপিআই আইও সমাপ্তি বন্দরগুলি ব্যবহার করে যা নেটওয়ার্কিং অপারেশন সম্পাদন করার দ্রুততম উপায়, http://msdn.microsoft.com/en-us/magazine/cc302334.aspx দেখুন

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

 public class Telnet
{
    private readonly Pool<SocketAsyncEventArgs> m_EventArgsPool;
    private Socket m_ListenSocket;

    /// <summary>
    /// This event fires when a connection has been established.
    /// </summary>
    public event EventHandler<SocketAsyncEventArgs> Connected;

    /// <summary>
    /// This event fires when a connection has been shutdown.
    /// </summary>
    public event EventHandler<SocketAsyncEventArgs> Disconnected;

    /// <summary>
    /// This event fires when data is received on the socket.
    /// </summary>
    public event EventHandler<SocketAsyncEventArgs> DataReceived;

    /// <summary>
    /// This event fires when data is finished sending on the socket.
    /// </summary>
    public event EventHandler<SocketAsyncEventArgs> DataSent;

    /// <summary>
    /// This event fires when a line has been received.
    /// </summary>
    public event EventHandler<LineReceivedEventArgs> LineReceived;

    /// <summary>
    /// Specifies the port to listen on.
    /// </summary>
    [DefaultValue(23)]
    public int ListenPort { get; set; }

    /// <summary>
    /// Constructor for Telnet class.
    /// </summary>
    public Telnet()
    {           
        m_EventArgsPool = new Pool<SocketAsyncEventArgs>();
        ListenPort = 23;
    }

    /// <summary>
    /// Starts the telnet server listening and accepting data.
    /// </summary>
    public void Start()
    {
        IPEndPoint endpoint = new IPEndPoint(0, ListenPort);
        m_ListenSocket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        m_ListenSocket.Bind(endpoint);
        m_ListenSocket.Listen(100);

        //
        // Post Accept
        //
        StartAccept(null);
    }

    /// <summary>
    /// Not Yet Implemented. Should shutdown all connections gracefully.
    /// </summary>
    public void Stop()
    {
        //throw (new NotImplementedException());
    }

    //
    // ACCEPT
    //

    /// <summary>
    /// Posts a requests for Accepting a connection. If it is being called from the completion of
    /// an AcceptAsync call, then the AcceptSocket is cleared since it will create a new one for
    /// the new user.
    /// </summary>
    /// <param name="e">null if posted from startup, otherwise a <b>SocketAsyncEventArgs</b> for reuse.</param>
    private void StartAccept(SocketAsyncEventArgs e)
    {
        if (e == null)
        {
            e = m_EventArgsPool.Pop();
            e.Completed += Accept_Completed;
        }
        else
        {
            e.AcceptSocket = null;
        }

        if (m_ListenSocket.AcceptAsync(e) == false)
        {
            Accept_Completed(this, e);
        }
    }

    /// <summary>
    /// Completion callback routine for the AcceptAsync post. This will verify that the Accept occured
    /// and then setup a Receive chain to begin receiving data.
    /// </summary>
    /// <param name="sender">object which posted the AcceptAsync</param>
    /// <param name="e">Information about the Accept call.</param>
    private void Accept_Completed(object sender, SocketAsyncEventArgs e)
    {
        //
        // Socket Options
        //
        e.AcceptSocket.NoDelay = true;

        //
        // Create and setup a new connection object for this user
        //
        Connection connection = new Connection(this, e.AcceptSocket);

        //
        // Tell the client that we will be echo'ing data sent
        //
        DisableEcho(connection);

        //
        // Post the first receive
        //
        SocketAsyncEventArgs args = m_EventArgsPool.Pop();
        args.UserToken = connection;

        //
        // Connect Event
        //
        if (Connected != null)
        {
            Connected(this, args);
        }

        args.Completed += Receive_Completed;
        PostReceive(args);

        //
        // Post another accept
        //
        StartAccept(e);
    }

    //
    // RECEIVE
    //    

    /// <summary>
    /// Post an asynchronous receive on the socket.
    /// </summary>
    /// <param name="e">Used to store information about the Receive call.</param>
    private void PostReceive(SocketAsyncEventArgs e)
    {
        Connection connection = e.UserToken as Connection;

        if (connection != null)
        {
            connection.ReceiveBuffer.EnsureCapacity(64);
            e.SetBuffer(connection.ReceiveBuffer.DataBuffer, connection.ReceiveBuffer.Count, connection.ReceiveBuffer.Remaining);

            if (connection.Socket.ReceiveAsync(e) == false)
            {
                Receive_Completed(this, e);
            }              
        }
    }

    /// <summary>
    /// Receive completion callback. Should verify the connection, and then notify any event listeners
    /// that data has been received. For now it is always expected that the data will be handled by the
    /// listeners and thus the buffer is cleared after every call.
    /// </summary>
    /// <param name="sender">object which posted the ReceiveAsync</param>
    /// <param name="e">Information about the Receive call.</param>
    private void Receive_Completed(object sender, SocketAsyncEventArgs e)
    {
        Connection connection = e.UserToken as Connection;

        if (e.BytesTransferred == 0 || e.SocketError != SocketError.Success || connection == null)
        {
            Disconnect(e);
            return;
        }

        connection.ReceiveBuffer.UpdateCount(e.BytesTransferred);

        OnDataReceived(e);

        HandleCommand(e);
        Echo(e);

        OnLineReceived(connection);

        PostReceive(e);
    }

    /// <summary>
    /// Handles Event of Data being Received.
    /// </summary>
    /// <param name="e">Information about the received data.</param>
    protected void OnDataReceived(SocketAsyncEventArgs e)
    {
        if (DataReceived != null)
        {                
            DataReceived(this, e);
        }
    }

    /// <summary>
    /// Handles Event of a Line being Received.
    /// </summary>
    /// <param name="connection">User connection.</param>
    protected void OnLineReceived(Connection connection)
    {
        if (LineReceived != null)
        {
            int index = 0;
            int start = 0;

            while ((index = connection.ReceiveBuffer.IndexOf('\n', index)) != -1)
            {
                string s = connection.ReceiveBuffer.GetString(start, index - start - 1);
                s = s.Backspace();

                LineReceivedEventArgs args = new LineReceivedEventArgs(connection, s);
                Delegate[] delegates = LineReceived.GetInvocationList();

                foreach (Delegate d in delegates)
                {
                    d.DynamicInvoke(new object[] { this, args });

                    if (args.Handled == true)
                    {
                        break;
                    }
                }

                if (args.Handled == false)
                {
                    connection.CommandBuffer.Enqueue(s);
                }

                start = index;
                index++;
            }

            if (start > 0)
            {
                connection.ReceiveBuffer.Reset(0, start + 1);
            }
        }
    }

    //
    // SEND
    //

    /// <summary>
    /// Overloaded. Sends a string over the telnet socket.
    /// </summary>
    /// <param name="connection">Connection to send data on.</param>
    /// <param name="s">Data to send.</param>
    /// <returns>true if the data was sent successfully.</returns>
    public bool Send(Connection connection, string s)
    {
        if (String.IsNullOrEmpty(s) == false)
        {
            return Send(connection, Encoding.Default.GetBytes(s));
        }

        return false;
    }

    /// <summary>
    /// Overloaded. Sends an array of data to the client.
    /// </summary>
    /// <param name="connection">Connection to send data on.</param>
    /// <param name="data">Data to send.</param>
    /// <returns>true if the data was sent successfully.</returns>
    public bool Send(Connection connection, byte[] data)
    {
        return Send(connection, data, 0, data.Length);
    }

    public bool Send(Connection connection, char c)
    {
        return Send(connection, new byte[] { (byte)c }, 0, 1);
    }

    /// <summary>
    /// Sends an array of data to the client.
    /// </summary>
    /// <param name="connection">Connection to send data on.</param>
    /// <param name="data">Data to send.</param>
    /// <param name="offset">Starting offset of date in the buffer.</param>
    /// <param name="length">Amount of data in bytes to send.</param>
    /// <returns></returns>
    public bool Send(Connection connection, byte[] data, int offset, int length)
    {
        bool status = true;

        if (connection.Socket == null || connection.Socket.Connected == false)
        {
            return false;
        }

        SocketAsyncEventArgs args = m_EventArgsPool.Pop();
        args.UserToken = connection;
        args.Completed += Send_Completed;
        args.SetBuffer(data, offset, length);

        try
        {
            if (connection.Socket.SendAsync(args) == false)
            {
                Send_Completed(this, args);
            }
        }
        catch (ObjectDisposedException)
        {                
            //
            // return the SocketAsyncEventArgs back to the pool and return as the
            // socket has been shutdown and disposed of
            //
            m_EventArgsPool.Push(args);
            status = false;
        }

        return status;
    }

    /// <summary>
    /// Sends a command telling the client that the server WILL echo data.
    /// </summary>
    /// <param name="connection">Connection to disable echo on.</param>
    public void DisableEcho(Connection connection)
    {
        byte[] b = new byte[] { 255, 251, 1 };
        Send(connection, b);
    }

    /// <summary>
    /// Completion callback for SendAsync.
    /// </summary>
    /// <param name="sender">object which initiated the SendAsync</param>
    /// <param name="e">Information about the SendAsync call.</param>
    private void Send_Completed(object sender, SocketAsyncEventArgs e)
    {
        e.Completed -= Send_Completed;              
        m_EventArgsPool.Push(e);
    }        

    /// <summary>
    /// Handles a Telnet command.
    /// </summary>
    /// <param name="e">Information about the data received.</param>
    private void HandleCommand(SocketAsyncEventArgs e)
    {
        Connection c = e.UserToken as Connection;

        if (c == null || e.BytesTransferred < 3)
        {
            return;
        }

        for (int i = 0; i < e.BytesTransferred; i += 3)
        {
            if (e.BytesTransferred - i < 3)
            {
                break;
            }

            if (e.Buffer[i] == (int)TelnetCommand.IAC)
            {
                TelnetCommand command = (TelnetCommand)e.Buffer[i + 1];
                TelnetOption option = (TelnetOption)e.Buffer[i + 2];

                switch (command)
                {
                    case TelnetCommand.DO:
                        if (option == TelnetOption.Echo)
                        {
                            // ECHO
                        }
                        break;
                    case TelnetCommand.WILL:
                        if (option == TelnetOption.Echo)
                        {
                            // ECHO
                        }
                        break;
                }

                c.ReceiveBuffer.Remove(i, 3);
            }
        }          
    }

    /// <summary>
    /// Echoes data back to the client.
    /// </summary>
    /// <param name="e">Information about the received data to be echoed.</param>
    private void Echo(SocketAsyncEventArgs e)
    {
        Connection connection = e.UserToken as Connection;

        if (connection == null)
        {
            return;
        }

        //
        // backspacing would cause the cursor to proceed beyond the beginning of the input line
        // so prevent this
        //
        string bs = connection.ReceiveBuffer.ToString();

        if (bs.CountAfterBackspace() < 0)
        {
            return;
        }

        //
        // find the starting offset (first non-backspace character)
        //
        int i = 0;

        for (i = 0; i < connection.ReceiveBuffer.Count; i++)
        {
            if (connection.ReceiveBuffer[i] != '\b')
            {
                break;
            }
        }

        string s = Encoding.Default.GetString(e.Buffer, Math.Max(e.Offset, i), e.BytesTransferred);

        if (connection.Secure)
        {
            s = s.ReplaceNot("\r\n\b".ToCharArray(), '*');
        }

        s = s.Replace("\b", "\b \b");

        Send(connection, s);
    }

    //
    // DISCONNECT
    //

    /// <summary>
    /// Disconnects a socket.
    /// </summary>
    /// <remarks>
    /// It is expected that this disconnect is always posted by a failed receive call. Calling the public
    /// version of this method will cause the next posted receive to fail and this will cleanup properly.
    /// It is not advised to call this method directly.
    /// </remarks>
    /// <param name="e">Information about the socket to be disconnected.</param>
    private void Disconnect(SocketAsyncEventArgs e)
    {
        Connection connection = e.UserToken as Connection;

        if (connection == null)
        {
            throw (new ArgumentNullException("e.UserToken"));
        }

        try
        {
            connection.Socket.Shutdown(SocketShutdown.Both);
        }
        catch
        {
        }

        connection.Socket.Close();

        if (Disconnected != null)
        {
            Disconnected(this, e);
        }

        e.Completed -= Receive_Completed;
        m_EventArgsPool.Push(e);
    }

    /// <summary>
    /// Marks a specific connection for graceful shutdown. The next receive or send to be posted
    /// will fail and close the connection.
    /// </summary>
    /// <param name="connection"></param>
    public void Disconnect(Connection connection)
    {
        try
        {
            connection.Socket.Shutdown(SocketShutdown.Both);
        }
        catch (Exception)
        {
        }            
    }

    /// <summary>
    /// Telnet command codes.
    /// </summary>
    internal enum TelnetCommand
    {
        SE = 240,
        NOP = 241,
        DM = 242,
        BRK = 243,
        IP = 244,
        AO = 245,
        AYT = 246,
        EC = 247,
        EL = 248,
        GA = 249,
        SB = 250,
        WILL = 251,
        WONT = 252,
        DO = 253,
        DONT = 254,
        IAC = 255
    }

    /// <summary>
    /// Telnet command options.
    /// </summary>
    internal enum TelnetOption
    {
        Echo = 1,
        SuppressGoAhead = 3,
        Status = 5,
        TimingMark = 6,
        TerminalType = 24,
        WindowSize = 31,
        TerminalSpeed = 32,
        RemoteFlowControl = 33,
        LineMode = 34,
        EnvironmentVariables = 36
    }
}

এটি বেশ সোজা এগিয়ে, এবং একটি সহজ উদাহরণ। ধন্যবাদ। আমি প্রতিটি পদ্ধতির প্রো এবং কনসের মূল্যায়ন করতে যাচ্ছি।
এরিক ফানকেনবাশ

আমি এটি পরীক্ষা করার সুযোগ পাইনি তবে আমি এখানে কোনও কারণে দৌড়ের অবস্থা সম্পর্কে অস্পষ্ট অনুভূতি পাচ্ছি। প্রথমত, আপনি যদি প্রচুর বার্তা পান তবে আমি জানি না যে ইভেন্টগুলি ক্রমে প্রক্রিয়া করা হবে (ব্যবহারকারীদের অ্যাপ্লিকেশনটির জন্য গুরুত্বপূর্ণ নাও হতে পারে, তবে লক্ষ্য করা উচিত) অথবা আমার ভুল হতে পারে এবং ইভেন্টগুলি ক্রমক্রমে প্রক্রিয়াধীন হবে। দ্বিতীয়টি হ'ল আমি এটি মিস করেছি তবে ডেটা রিসিপড এখনও দীর্ঘ সময় লাগলে চলমান অবস্থায় বাফার ওভাররাইট মুছে ফেলার ঝুঁকি নেই? যদি এই সম্ভবত অযৌক্তিক উদ্বেগের সমাধান করা হয় তবে আমি মনে করি এটি একটি খুব ভাল আধুনিক সমাধান।
কেভিন নিসবেট

1
আমার ক্ষেত্রে, আমার টেলনেট সার্ভারের জন্য, 100%, হ্যাঁ তারা ক্রমযুক্ত। কীটি অ্যাকসেপ্টএেন্স, রিসিভএসিঙ্ক ইত্যাদি কল করার আগে যথাযথ কলব্যাক পদ্ধতিটি সেট করে my এটি সংশোধন করা প্রয়োজন।
এসাক

1
পয়েন্ট # 2 হ'ল এমন কিছু যা আপনার বিবেচনায় নেওয়া উচিত। আমি আমার 'সংযোগ' অবজেক্টটি সকেটঅ্যাসেন্সএভেন্টআর্গস প্রসঙ্গে সংরক্ষণ করছি। এর অর্থ হ'ল আমার কাছে কেবল সংযোগের জন্য একটি বাফার রয়েছে। ডেটা রিসিপ্ট সম্পন্ন না হওয়া পর্যন্ত আমি এই সকেটএসিএনসিএন্টএআরগসের সাথে আর কোনও প্রাপ্তি পোস্ট করছি না, সুতরাং এটি সম্পূর্ণ না হওয়া পর্যন্ত আর কোনও ডেটা পড়া যাবে না। আমি ADVISE করি যে এই ডেটা উপর আর দীর্ঘ অপারেশন করা হবে। আমি আসলে প্রাপ্ত সমস্ত ডেটার পুরো বাফারটিকে একটি লকফ্রি সারিতে স্থানান্তরিত করি এবং তারপরে এটি একটি পৃথক থ্রেডে প্রক্রিয়া করি। এটি নেটওয়ার্ক অংশে কম বিলম্বিতা নিশ্চিত করে।
এসাক

1
একদিকে নোটে, আমি এই কোডের জন্য ইউনিট পরীক্ষা এবং লোড পরীক্ষা লিখেছি এবং ব্যবহারকারীর লোড 1 ব্যবহারকারী থেকে 250 জন (একক ডুয়াল কোর সিস্টেমে, 4 গিগাবাইট র‌্যাম) এর মধ্যে 100 বাইটের প্রতিক্রিয়া সময় (1 প্যাকেট) এবং 10000 বাইট (3 প্যাকেট) পুরো ব্যবহারকারীর লোড বক্ররেখায় একই ছিল।
এসাক

46

স্কেবলযোগ্য টিসিপি / আইপি ব্যবহার করে সেখানে খুব ভাল আলোচনা হত Covers এই থ্রেডে তার উপস্থিতি: সি ++ বনাম সি #: একটি অত্যন্ত স্কেলযোগ্য IOCP সার্ভার বিকাশ করা )

প্রথম এবং সর্বাগ্রে, নোট করুন যে ক্লাসে ব্যবহার Begin/Endএবং Asyncপদ্ধতি উভয়ই Socketআইও কমপ্লিটেশন পোর্টস (আইওসিপি) ব্যবহার করে স্কেলিবিলিটি সরবরাহ করে। আপনার সমাধানটি বাস্তবায়নের জন্য আপনি যে দুটি পদ্ধতির প্রকৃতপক্ষে বেছে নিয়েছেন তার তুলনায় স্কেল্যাবিলিটি (এটি সঠিকভাবে ব্যবহৃত হলে; নীচে দেখুন) অনেক বড় তফাত তৈরি করে।

ক্রিস মুলিনসের পোস্টগুলি ব্যবহারের উপর ভিত্তি করে তৈরি হয়েছিল Begin/End, এটিই আমার ব্যক্তিগতভাবে অভিজ্ঞতা আছে। নোট করুন যে ক্রিস এই ভিত্তিতে একটি সমাধান রেখেছিলেন যা 2 জিবি মেমরির সাথে 32-বিট মেশিনে 10,000s সমবর্তী ক্লায়েন্ট সংযোগ মাপিয়েছিল এবং পর্যাপ্ত মেমরির সাথে একটি 64-বিট প্ল্যাটফর্মে 100,000 এর মধ্যে রয়েছে। এই কৌশলটির সাথে আমার নিজের অভিজ্ঞতা থেকে (এই ধরণের লোডের কাছাকাছি কোথাও কোথাও) আমার এই সূচকগুলি দেখাতে সন্দেহ করার কোনও কারণ নেই।

আইওসিপি বনাম থ্রেড-প্রতি-সংযোগ বা 'নির্বাচন করুন' আদিম

আপনি যে পদ্ধতিটি হুডের নীচে আইওসিপি ব্যবহার করতে চান তা হ'ল এটি খুব নিম্ন স্তরের উইন্ডোজ থ্রেড পুল ব্যবহার করে যা আপনি যে আইও চ্যানেল থেকে পড়ার চেষ্টা করছেন সেখানে প্রকৃত ডেটা না পাওয়া পর্যন্ত কোনও থ্রেড জাগ্রত করে না ( নোট করুন আইওসিপি ফাইল আইও হিসাবেও ব্যবহার করা যেতে পারে)। এর সুবিধাটি হ'ল উইন্ডোজকে কেবল কোনও থ্রেডে স্যুইচ করতে হবে না যে কোনও উপাত্ত এখনও পাওয়া যায় নি, সুতরাং এটি আপনার সার্ভারকে প্রাসঙ্গিক সুইচগুলি কম ন্যূনতম প্রয়োজনীয় করতে হবে তা হ্রাস করে।

প্রসঙ্গের স্যুইচগুলি হ'ল 'থ্রেড-প্রতি-সংযোগ' প্রক্রিয়াটি অবশ্যই স্পষ্টভাবে মেরে ফেলবে, যদিও আপনি কেবল কয়েক ডজন সংযোগ নিয়ে কাজ করছেন এটি এটি একটি কার্যকর সমাধান। এই মেকানিজমটি অবশ্য কল্পনা 'স্কেলেবল' এর কোনও প্রসার নয়।

আইওসিপি ব্যবহার করার সময় গুরুত্বপূর্ণ বিবেচনাগুলি

স্মৃতি

প্রথম এবং সর্বাগ্রে এটা বোঝা গুরুত্বপূর্ণ যে IOCP সহজেই .NET এর অধীনে মেমরির সমস্যার সমাধান করতে পারে যদি আপনার বাস্তবায়ন খুব নিরীহ হয়। প্রতিটি আইওসিপি BeginReceiveকল আপনি যে বাফারে পড়ছেন তার "পিনিং" এর ফলস্বরূপ। কেন এটি সমস্যা তা সম্পর্কে একটি ভাল ব্যাখ্যার জন্য, দেখুন: ইউন জিনের ওয়েবলগ: আউট অফ মেমরি এক্সসেপশন এবং পিনিং

ভাগ্যক্রমে এই সমস্যা এড়ানো যেতে পারে তবে এর জন্য কিছুটা বাণিজ্য বন্ধ করা দরকার। প্রস্তাবিত সমাধানটি হ'ল byte[]অ্যাপ্লিকেশন শুরুর সময় একটি বড় বাফার বরাদ্দ করা (বা এটি বন্ধ করে দেওয়া), কমপক্ষে 90KB বা তাই (। নেট 2 হিসাবে, প্রয়োজনীয় আকারগুলি পরবর্তী সংস্করণগুলিতে বড় হতে পারে)। এটি করার কারণটি হ'ল বড় মেমরির বরাদ্দগুলি স্বয়ংক্রিয়ভাবে স্বয়ংক্রিয়ভাবে পিন করা একটি অ-কমপ্যাক্ট মেমরি বিভাগে (বৃহত্তর অবজেক্ট হিপ) শেষ হয়। প্রারম্ভকালে একটি বড় বাফার বরাদ্দ করে আপনি নিশ্চিত করেছেন যে অস্থাবর মেমরির এই ব্লকটি তুলনামূলকভাবে 'কম ঠিকানা' এ রয়েছে যেখানে এটি পথে না যায় এবং খণ্ডন ঘটায়।

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

সবচেয়ে সহজ সমাধান হ'ল এই বাফারের মধ্যে একটি অনন্য অফসেটে প্রতিটি সংযোগকে একক বাইট বরাদ্দ করা। তারপরে আপনি BeginReceiveএকক বাইট পড়ার জন্য একটি কল করতে পারেন এবং আপনি যে কলব্যাক পেয়েছেন তার ফলস্বরূপ বাকী পড়াটি সম্পাদন করতে পারেন।

প্রসেসিং

আপনি যে Beginকল করেছেন তার থেকে কলব্যাক পেলে, কলব্যাকের কোডটি নিম্ন স্তরের আইওসিপি থ্রেডে কার্যকর হবে তা উপলব্ধি করা খুব গুরুত্বপূর্ণ। আপনি এই কলব্যাকের দীর্ঘ ক্রিয়াকলাপ এড়ানোর পক্ষে একেবারে অপরিহার্য । জটিল প্রসেসিংয়ের জন্য এই থ্রেডগুলি ব্যবহার করা আপনার স্কেলাবিলিটিটিকে ঠিক 'থ্রেড-প্রতি-সংযোগ' ব্যবহারের মতো কার্যকরভাবে হত্যা করবে।

প্রস্তাবিত সমাধানটি হ'ল কলব্যাকটি কেবলমাত্র কোনও ইনকামিং ডেটা প্রক্রিয়া করার জন্য কোনও কাজের আইটেম সারি করার জন্য কলব্যাক ব্যবহার করা হবে, এটি অন্য কোনও থ্রেডে কার্যকর করা হবে। কলব্যাকের অভ্যন্তরে কোনও সম্ভাব্য ব্লকিং অপারেশন এড়িয়ে চলুন যাতে আইওসিপি থ্রেড যত তাড়াতাড়ি সম্ভব তার পুলে ফিরে আসতে পারে। .NET 4.0-এ আমি পরামর্শ দিচ্ছি যে সহজ সমাধানটি হ'ল এ Task, এটি ক্লায়েন্ট সকেটের একটি রেফারেন্স এবং প্রথম বাইটের একটি অনুলিপি যা ইতিমধ্যে BeginReceiveকল দ্বারা পড়া হয়েছিল । এই কাজটি সকেট থেকে সমস্ত ডেটা পড়ার জন্য দায়বদ্ধ যা আপনি প্রক্রিয়াকরণের অনুরোধটি উপস্থাপন করছেন, এটি সম্পাদন করছেন এবং তারপরে BeginReceiveআইওসিপি-র জন্য সকেটকে আরও একবার কাতারে একটি নতুন কল করার জন্য। .NET 4.0 পূর্ব, আপনি থ্রেডপুল ব্যবহার করতে পারেন, বা আপনার নিজস্ব থ্রেডেড ওয়ার্ক-সারি বাস্তবায়ন তৈরি করতে পারেন।

সারসংক্ষেপ

মূলত, আমি নিম্নলিখিত যুক্ত সতর্কতা সহ এই সমাধানের জন্য কেভিনের নমুনা কোডটি ব্যবহার করার পরামর্শ দেব:

  • নিশ্চিত হয়ে নিন যে আপনি যে বাফারটি পাস করেছেন BeginReceiveসেটি ইতিমধ্যে 'পিনড' রয়েছে
  • নিশ্চিত হয়ে নিন যে আপনি যে কলব্যাকটি পাস করেছেন BeginReceiveতা আগত ডেটার প্রকৃত প্রসেসিং পরিচালনা করতে কোনও কাজ সারি করা ছাড়া আর কিছু না করে

আপনি যখন এটি করেন, আমার সন্দেহ নেই যে আপনি ক্রিসের ফলাফলগুলি একই সাথে কয়েক সহস্র হাজার একসাথে ক্লায়েন্টকে অঙ্কন করতে পেরেছিলেন (সঠিক হার্ডওয়্যার এবং আপনার নিজস্ব প্রসেসিং কোডের কার্যকর প্রয়োগের জন্য;)


1
মেমরির একটি ছোট ব্লকটি পিন করতে, বাফারটি পিন করতে GCHandle অবজেক্ট অলোক পদ্ধতি ব্যবহার করা যেতে পারে। এটি সম্পন্ন হয়ে গেলে মার্শাল অবজেক্টের অনিরাপদ অ্যাডডরঅফপিন্ডড্রাইয়ের উপাদানটি বাফারের পয়েন্টার পেতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ: GCHandle gchTheCards = GCHandle.Alloc (TheData, GCHandleType.Pinned); ইন্টারপ্রেট পিএডিডিআর = মার্শাল।উনস্যাফএএডড্রঅফপিনডআররেইলেট (থিডাটা, 0); (sbyte *) pTheData = (sbyte *) pAddr.ToPointer ();
বব ব্রায়ান

@ Bobry ব্রায়ান যদি না আপনি যে সূক্ষ্ম বিন্দুটি বানাতে চাইছেন তা মিস না করে, এই পদ্ধতির সাহায্যে আমার সমস্যাটি বড় ব্লকগুলি বরাদ্দ করে সমস্যাটির সমাধান করার চেষ্টা করছে যা আসলে ছোট পিনযুক্ত ব্লকের পুনরাবৃত্ত বরাদ্দের অন্তর্ভুক্ত নাটকীয় মেমরির বিভাজনের সম্ভাবনা স্মৃতি।
jerryjvl

ঠিক আছে, মুল বক্তব্যটি হ'ল এটিকে স্মৃতিতে পিন করার জন্য আপনাকে একটি বৃহত ব্লক বরাদ্দ করতে হবে না। আপনি ছোট ব্লকগুলি বরাদ্দ করতে এবং উপরের কৌশলটি মেমরিটিতে পিন করতে ব্যবহার করতে পারেন যাতে গিসি এগুলি সরাতে না পারে। আপনি ছোট ছোট প্রতিটি ব্লকের একটি রেফারেন্স রাখতে পারেন, যেমন আপনি কোনও একটি বৃহত্তর ব্লকের রেফারেন্স রাখেন এবং প্রয়োজনীয় হিসাবে সেগুলি পুনরায় ব্যবহার করুন। হয় পদ্ধতির বৈধতা - আমি কেবল ইঙ্গিত করছিলাম যে আপনাকে খুব বড় বাফার ব্যবহার করতে হবে না। তবে, যেহেতু জিসি আরও দক্ষতার সাথে চিকিত্সা করবে সেহেতু কখনও কখনও খুব বড় বাফার ব্যবহার করা সবচেয়ে ভাল উপায়।
বব ব্রায়ান

আপনি বাজনার পিনিং করার পরে @ BobBryan স্বয়ংক্রিয়ভাবে ঘটবে যখন আপনি বিগিনিগ্রিভ কল করবেন, পিনিং এখানে প্রকৃতপক্ষে মূল বিষয় নয়; দক্ষতা ছিল;) ... এবং স্কেলযোগ্য সার্ভারটি লেখার চেষ্টা করার সময় এটি বিশেষত উদ্বেগজনক, সুতরাং বাফার স্পেসের জন্য বড় ব্লক বরাদ্দ করা দরকার।
jerryjvl

@ জেরিজভল সত্যিই একটি পুরানো প্রশ্ন আনার জন্য দুঃখিত, তবে আমি সম্প্রতি বিগিনএক্সএক্সএক্সএক্স / এন্ডএক্সএক্সএক্সএক্স অ্যাসিচ পদ্ধতিতে এই সঠিক সমস্যাটি আবিষ্কার করেছি। এটি দুর্দান্ত পোস্ট, তবে এটি অনুসন্ধানের জন্য প্রচুর খনক নিয়েছে। আমি আপনার প্রস্তাবিত সমাধানটি পছন্দ করি তবে এর একটি অংশ বুঝতে পারি না: "তারপরে আপনি একক বাইট পড়ার জন্য একটি বিগিনি রিসিভ কল করতে পারেন এবং আপনি যে কলব্যাক পেয়েছেন তার ফলস্বরূপ বাকী পড়াটি সম্পাদন করতে পারেন।" আপনি যে কলব্যাক পাচ্ছেন তার ফলস্বরূপ বাকী পড়াটি করা বলতে কী বোঝ?
মাউসিমো

22

আপনি ইতিমধ্যে উপরের কোড নমুনাগুলির মাধ্যমে উত্তরের বেশিরভাগ অংশ পেয়েছেন। অ্যাসিঙ্ক্রোনাস আইও অপারেশন ব্যবহার করা একেবারে এখানে যাওয়ার উপায়। উইন 32 স্কেল করার জন্য অভ্যন্তরীণভাবে ডিজাইন করা হয়েছে এ্যাসিঙ্ক আইও। আপনার প্রাপ্ত সেরা সম্ভাব্য পারফরম্যান্স সম্পূর্ণ পোর্টগুলি ব্যবহার করে অর্জিত হয়, আপনার সকেটগুলি সমাপ্তি বন্দরগুলিতে আবদ্ধ করে এবং একটি থ্রেড পুল সমাপ্তি বন্দর সমাপ্তির জন্য অপেক্ষা করে। সাধারণ জ্ঞানের সিপিইউতে 2-4 থ্রেড (কোর) সমাপ্তির জন্য অপেক্ষা করা থাকে। আমি উইন্ডোজ পারফরম্যান্স দল থেকে রিক ভিকিকের এই তিনটি নিবন্ধটি সন্ধান করার জন্য আমি উচ্চতর পরামর্শ দিচ্ছি:

  1. পারফরম্যান্সের জন্য অ্যাপ্লিকেশনগুলি ডিজাইনিং - পার্ট 1
  2. পারফরম্যান্সের জন্য অ্যাপ্লিকেশনগুলি ডিজাইনিং - পার্ট 2
  3. পারফরম্যান্সের জন্য অ্যাপ্লিকেশনগুলি ডিজাইনিং - পার্ট 3

উল্লিখিত নিবন্ধগুলি বেশিরভাগ নেটিভ উইন্ডোজ এপিআইকে অন্তর্ভুক্ত করে তবে স্কেলাবিলিটি এবং পারফরম্যান্সের বিষয়ে উপলব্ধি অর্জনের চেষ্টা করা যে কোনও ব্যক্তির জন্য এগুলি অবশ্যই পড়তে হবে। জিনিসগুলির পরিচালিত দিকগুলিতেও তাদের কিছু সংক্ষিপ্ত বিবরণ রয়েছে।

দ্বিতীয়টি যা আপনাকে করতে হবে তা হ'ল আপনি অনলাইনে উপলব্ধ উন্নত। নেট অ্যাপ্লিকেশন পারফরম্যান্স এবং স্কেলাবিলিটি বইটি পেরিয়ে যাচ্ছেন তা নিশ্চিত করা । আপনি থ্রেড, অ্যাসিনক্রোনাস কল এবং লক ব্যবহারের চারপাশে প্রাসঙ্গিক এবং বৈধ পরামর্শ পাবেন Chapter অধ্যায়ের মধ্যে। তবে আসল রত্নগুলি অধ্যায় 17 এ রয়েছে যেখানে আপনি আপনার থ্রেড পুল টিউন করার বিষয়ে ব্যবহারিক দিকনির্দেশনার মতো গুডিজ পাবেন। আমি এই অধ্যায়ে সুপারিশ অনুসারে ম্যাক্সোইথ্রেডস / ম্যাক ওয়ার্কার থ্রেডস সামঞ্জস্য না করা পর্যন্ত আমার অ্যাপ্লিকেশনগুলিতে কিছু গুরুতর সমস্যা ছিল।

আপনি বলছেন যে আপনি খাঁটি টিসিপি সার্ভার করতে চান তাই আমার পরবর্তী পয়েন্টটি উত্সাহিত। তবে , আপনি যদি নিজেকে কোণঠাসা করে খুঁজে দেখতে এবং ওয়েবআরকোয়েস্ট ক্লাস এবং এর ডেরিভেটিভগুলি ব্যবহার করেন তবে সতর্ক হতে হবে যে সেই দরজাটি রক্ষণকারী একটি ড্রাগন রয়েছে: সার্ভিসপয়েন্ট ম্যানেজার । এটি একটি কনফিগারেশন শ্রেণি যা জীবনের একটি উদ্দেশ্য: আপনার কর্মক্ষমতা নষ্ট করা। নিশ্চিত করুন যে আপনি কৃত্রিম আরোপিত সার্ভিস পয়েন্ট থেকে আপনার সার্ভারটি মুক্ত করেছেন on সংযোগ লিমিট বা আপনার অ্যাপ্লিকেশনটি কখনই স্কেল হবে না (আমি আপনাকে নিজেকে আবিষ্কার করতে দেব যে ডিফল্ট মানটি কী ...)। আপনি HTTP অনুরোধগুলিতে Expect100Continue শিরোনাম প্রেরণের ডিফল্ট নীতিটি পুনর্বিবেচনা করতে পারেন।

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

আপনি বিগিনিডার / বিগিনিউইট এপিআইয়ের সাথে খেলতে পেরে এবং গুরুতর কাজ শুরু করার পরে আপনি বুঝতে পারবেন যে আপনার ট্র্যাফিকের সুরক্ষা প্রয়োজন, যেমন। এনটিএলএম / কার্বেরোস প্রমাণীকরণ এবং ট্র্যাফিক এনক্রিপশন, বা কমপক্ষে ট্র্যাফিক টেম্পারিং সুরক্ষা। আপনি যেভাবে এটি করেন তা হ'ল আপনি সিস্টেম ইন নেট.সিকিউরিটি.নিগোটিয়েটস্ট্রিম (বা এসএসলস্ট্রিম যদি আপনার পৃথক পৃথক ডোমেনগুলি যেতে হয় তবে) ব্যবহার করুন। এর অর্থ হ'ল স্ট্রেট সকেট অ্যাসিনক্রোনাস অপারেশনের উপর নির্ভর করার পরিবর্তে আপনি প্রমাণীকরণপ্রবাহ অ্যাসিঙ্ক্রোনাস অপারেশনের উপর নির্ভর করবেন। আপনি কোনও সকেট পাওয়ার সাথে সাথে (হয় ক্লায়েন্টের সাথে সংযোগ থেকে বা সার্ভারে স্বীকৃত হয়ে) আপনি সকেটে একটি স্ট্রিম তৈরি করেন এবং বিগিনিউথেকটিএটস্লিয়ার বা বিগলআউটথ্যান্টিএটস সার্ভারকে ফোন করে প্রমাণীকরণের জন্য জমা দিন। প্রমাণীকরণটি সম্পূর্ণ হওয়ার পরে (কমপক্ষে আপনার নেটিভ ইনিশিয়েটিকিউরিটি কনটেক্সট / অ্যাকসেপ্টসিকিউরিটি কনটেক্সট উন্মাদনা থেকে আপনার নিরাপদ ...) আপনি আপনার অনুমোদনপ্রাপ্ত স্ট্রিমের রিমোট আইডেন্টিটি সম্পত্তিটি যাচাই করে এবং আপনার পণ্যটি যে এসিএল যাচাইকরণকে সমর্থন করবে সেগুলি করে আপনার অনুমোদনটি করবেন। এরপরে আপনি বিগনিওরাইট ব্যবহার করে বার্তাগুলি প্রেরণ করবেন এবং আপনি সেগুলি বিগিনিডের মাধ্যমে পেয়ে যাবেন। এটির আগে আমি যে সমস্যার কথা বলছিলাম তা হ'ল আপনি একাধিক রিসিভ বাফার পোস্ট করতে পারবেন না, কারণ প্রমাণীকরণের স্ট্রিম ক্লাসগুলি এটি সমর্থন করে না। আপনি সম্পূর্ণ ফ্রেম না পাওয়া পর্যন্ত বিগিনিড অপারেশন অভ্যন্তরীণভাবে সমস্ত আইও পরিচালনা করে, অন্যথায় এটি বার্তা প্রমাণীকরণ (ডিক্রিপ্ট ফ্রেম এবং ফ্রেমে স্বাক্ষর বৈধকরণ) হ্যান্ডেল করতে পারে না। যদিও আমার অভিজ্ঞতায় প্রমাণীকৃত স্ট্রিম ক্লাসগুলির দ্বারা করা কাজ মোটামুটি ভাল এবং এটিতে কোনও সমস্যা হওয়া উচিত নয়। অর্থাৎ। আপনার কেবলমাত্র 4-5% সিপিইউ দিয়ে জিবি নেটওয়ার্ক পরিপূর্ণ করতে সক্ষম হওয়া উচিত। প্রমাণীকৃত স্ট্রিম ক্লাসগুলি আপনাকে প্রোটোকল নির্দিষ্ট ফ্রেম আকার সীমাবদ্ধতা (এসএসএল এর 16k, কার্বেরোসের জন্য 12 কে) চাপিয়ে দেবে।

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

বিটিডাব্লু নিশ্চিত করে নিন যে সার্ভারে বা ফাইল আইওতে আপনার যদি ডাটাবেসগুলির ক্রিয়াকলাপ রয়েছে তবে আপনি তাদের জন্য অ্যাসিঙ্ক স্বাদও ব্যবহার করেন, বা আপনি কোনওভাবেই থ্রেড পুলটি নিকাশ করবেন। এসকিউএল সার্ভারের জন্য সংযোগগুলি নিশ্চিত করুন যে আপনি সংযোগের স্ট্রিংয়ের সাথে 'এসিনক্রোনাস প্রসেসিং = ট্রু' যুক্ত করেছেন।


এখানে দুর্দান্ত কিছু তথ্য রয়েছে। আমি আশা করি আমি একাধিক লোককে অনুগ্রহ দিতে পারি। যাইহোক, আমি আপনাকে upvated করেছি। ধন্যবাদ এখানে ভাল জিনিস।
এরিক ফানকেনবাশ

11

আমার কয়েকটি সমাধানে আমি এমন একটি সার্ভার চালিয়েছি। এটি করার বিভিন্ন উপায় সম্পর্কে খুব বিশদ বিবরণ এখানে দেওয়া হয়েছে। নেট:। নেট মধ্যে উচ্চ-পারফরম্যান্স সকেটগুলির সাথে তারের আরও নিকটবর্তী হন Get

ইদানীং আমি আমাদের কোডের উন্নতি করার উপায়গুলি খুঁজছি এবং এটি সন্ধান করব: " সর্বাধিক পারফরম্যান্স অর্জনের জন্য অ্যাসিঙ্ক্রোনাস নেটওয়ার্ক I / O ব্যবহার করে এমন অ্যাপ্লিকেশনগুলির দ্বারা ব্যবহৃত" সংস্করণ 3.5 এর সকেট পারফরম্যান্স বর্ধন ""

"এই বর্ধনের প্রধান বৈশিষ্ট্য হ'ল উচ্চ-ভলিউম অ্যাসিনক্রোনাস সকেট I / O চলাকালীন অবজেক্টগুলির পুনরাবৃত্ত বরাদ্দ এবং সমন্বয়কে এড়িয়ে যাওয়া। অসিনক্রোনাস সকেট I / O এর জন্য সকেট শ্রেণীর দ্বারা বর্তমানে প্রয়োগ করা শুরু / সমাপ্ত নকশার প্যাটার্নটি একটি সিস্টেমের প্রয়োজন। প্রতিটি অ্যাসিক্রোনাস সকেট ক্রিয়াকলাপের জন্য আইএএএনসিএনআরসাল্ট অবজেক্ট বরাদ্দ করা হবে। "

লিঙ্কটি অনুসরণ করলে আপনি পড়া চালিয়ে যেতে পারেন। আমি ব্যক্তিগতভাবে আগামীকাল তাদের নমুনা কোডটি যা পেয়েছি তার বিপরীতে এটি পরীক্ষা করব।

সম্পাদনা: এখানে আপনি তাই আপনি এটি কয়েক মিনিটের মধ্যে পরীক্ষা এবং কোড পুরনো যেতে পারেন ক্লায়েন্ট ও সার্ভারের নতুন 3.5 SocketAsyncEventArgs ব্যবহার করার জন্য কোড কাজ খুঁজে পেতে পারেন। এটি একটি সহজ পদ্ধতির তবে এটি আরও বৃহত্তর বাস্তবায়ন শুরু করার ভিত্তি। এছাড়াও প্রায় দুই বছর আগে এমএসডিএন ম্যাগাজিনের এই নিবন্ধটি একটি আকর্ষণীয় পাঠ ছিল।



9

আপনি কি কেবলমাত্র একটি ডাব্লুসিএফ নেট টিসিপি বাঁধাই এবং একটি প্রকাশ / সাবস্ক্রাইব নিদর্শন ব্যবহার বিবেচনা করেছেন? ডাব্লুসিএফ আপনাকে নদীর গভীরতানির্ণয়ের পরিবর্তে [বেশিরভাগ ক্ষেত্রে] আপনার ডোমেনে ফোকাস করার অনুমতি দেবে ..

আইডিজাইনের ডাউনলোড বিভাগে প্রচুর ডাব্লুসিএফ নমুনা এমনকি একটি প্রকাশ / সাবস্ক্রাইব কাঠামো পাওয়া যায় যা দরকারী হতে পারে: http://www.idesign.net


8

আমি একটি জিনিস সম্পর্কে ভাবছি:

আমি অবশ্যই প্রতিটি সংযোগের জন্য কোনও থ্রেড শুরু করতে চাই না।

কেন এমন? কমপক্ষে উইন্ডোজ 2000 সাল থেকে উইন্ডোজ কোনও অ্যাপ্লিকেশনটিতে কয়েকশ থ্রেড পরিচালনা করতে পারে I've আমি এটি সম্পন্ন করেছি, থ্রেডগুলি সিঙ্ক্রোনাইজ করার প্রয়োজন না হলে এটি কাজ করা সত্যিই সহজ। বিশেষত প্রদত্ত যে আপনি প্রচুর I / O করছেন (যাতে আপনি সিপিইউ-বাউন্ড হন না, এবং ডিস্ক বা নেটওয়ার্ক যোগাযোগের ক্ষেত্রে প্রচুর থ্রেড ব্লক হয়ে যায়), আমি এই বিধিনিষেধটি বুঝতে পারি না।

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

এখানে কোডটি রয়েছে - এটি কীভাবে চলমান দেখায় তা দেখতে http://mdpopescu.blogspot.com/2009/05/m Multi-threaded-server.html এ গিয়ে ছবিটিতে ক্লিক করুন।

সার্ভার শ্রেণি:

  public class Server
  {
    private static readonly TcpListener listener = new TcpListener(IPAddress.Any, 9999);

    public Server()
    {
      listener.Start();
      Console.WriteLine("Started.");

      while (true)
      {
        Console.WriteLine("Waiting for connection...");

        var client = listener.AcceptTcpClient();
        Console.WriteLine("Connected!");

        // each connection has its own thread
        new Thread(ServeData).Start(client);
      }
    }

    private static void ServeData(object clientSocket)
    {
      Console.WriteLine("Started thread " + Thread.CurrentThread.ManagedThreadId);

      var rnd = new Random();
      try
      {
        var client = (TcpClient) clientSocket;
        var stream = client.GetStream();
        while (true)
        {
          if (rnd.NextDouble() < 0.1)
          {
            var msg = Encoding.ASCII.GetBytes("Status update from thread " + Thread.CurrentThread.ManagedThreadId);
            stream.Write(msg, 0, msg.Length);

            Console.WriteLine("Status update from thread " + Thread.CurrentThread.ManagedThreadId);
          }

          // wait until the next update - I made the wait time so small 'cause I was bored :)
          Thread.Sleep(new TimeSpan(0, 0, rnd.Next(1, 5)));
        }
      }
      catch (SocketException e)
      {
        Console.WriteLine("Socket exception in thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, e);
      }
    }
  }

সার্ভার প্রধান প্রোগ্রাম:

namespace ManyThreadsServer
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      new Server();
    }
  }
}

ক্লায়েন্ট শ্রেণি:

  public class Client
  {
    public Client()
    {
      var client = new TcpClient();
      client.Connect(IPAddress.Loopback, 9999);

      var msg = new byte[1024];

      var stream = client.GetStream();
      try
      {
        while (true)
        {
          int i;
          while ((i = stream.Read(msg, 0, msg.Length)) != 0)
          {
            var data = Encoding.ASCII.GetString(msg, 0, i);
            Console.WriteLine("Received: {0}", data);
          }
        }
      }
      catch (SocketException e)
      {
        Console.WriteLine("Socket exception in thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, e);
      }
    }
  }

ক্লায়েন্ট প্রধান প্রোগ্রাম:

using System;
using System.Threading;

namespace ManyThreadsClient
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      // first argument is the number of threads
      for (var i = 0; i < Int32.Parse(args[0]); i++)
        new Thread(RunClient).Start();
    }

    private static void RunClient()
    {
      new Client();
    }
  }
}

উইন্ডোজ প্রচুর থ্রেড পরিচালনা করতে পারে, কিন্তু। নেট তাদের হ্যান্ডেল করার জন্য সত্যই ডিজাইন করা হয় নি। প্রতিটি। নেট অ্যাপডোমেনের একটি থ্রেড পুল রয়েছে এবং আপনি এই থ্রেড পুলটি নিষ্কাশন করতে চান না। আমি নিশ্চিত না যে আপনি থ্রেডপুল থেকে আসে কিনা তা যদি নিজে নিজে কোনও থ্রেড শুরু করেন বা না করেন। তবুও, বেশিরভাগ সময়ের জন্য কিছুই না করে কয়েকশ থ্রেড একটি বিশাল সংস্থান অপচয়।
এরিক ফানকেনবাশ

1
আমি বিশ্বাস করি আপনার থ্রেডগুলির একটি ভুল দৃষ্টিভঙ্গি রয়েছে। থ্রেডগুলি কেবল থ্রেড পুল থেকে আসে যদি আপনি আসলে তা চান - নিয়মিত থ্রেডগুলি না করে। কয়েকশ থ্রেড কিছুই হ'ল কিছুই নষ্ট করে না :) (ভাল, কিছুটা মেমোরি, তবে মেমরিটি এত সস্তা যে এটি আসলে কোনও সমস্যা নয়।) আমি এর জন্য বেশ কয়েকটি নমুনা অ্যাপ্লিকেশন লিখতে চলেছি, আমি এখানে একটি ইউআরএল পোস্ট করব আমি একবার হয়ে গেছে। ইতিমধ্যে আমি আপনাকে সুপারিশ করছি যে আমি উপরে যা লিখেছি তা আবার ফিরে যান এবং আমার প্রশ্নের উত্তর দেওয়ার চেষ্টা করুন।
মার্সেল পপেস্কু

1
থ্রেডপুল থেকে থ্রেডপুল থেকে আসে না এমন থ্রেডগুলির থ্রেডগুলির দৃষ্টিভঙ্গি সম্পর্কে আমি মার্সেলের মন্তব্যে একমত হয়েছি, বাকী বাক্যটি সঠিক নয়। মেশিনটিতে কোনও মেশিনে কতটা ইনস্টল করা হয় তা নয়, উইন্ডোতে সমস্ত অ্যাপ্লিকেশন ভার্চুয়াল ঠিকানা স্পেসে এবং একটি 32 বিট সিস্টেমে চালিত হয় যা আপনাকে আপনার অ্যাপ্লিকেশনটির জন্য ডেটা 2 জিবি দেয় (বাক্সে কতটা র্যাম ইনস্টল করা হয় তা বিবেচনা করে না)। এগুলি অবশ্যই রানটাইম দ্বারা পরিচালনা করা উচিত। অ্যাসিঙ্ক আইও করা অপেক্ষার জন্য কোনও থ্রেড ব্যবহার করে না (এটি আইওসিপি ব্যবহার করে যা ওভারল্যাপযুক্ত আইওকে অনুমতি দেয়) এবং এটি একটি আরও ভাল সমাধান এবং আরও ভাল স্কেল করবে।
ব্রায়ান ওনিল

7
প্রচুর থ্রেড চালানোর সময় এটি মেমরি নয় যা সিপিইউ। থ্রেডগুলির মধ্যে প্রসঙ্গের স্যুইচ একটি তুলনামূলকভাবে ব্যয়বহুল ক্রিয়াকলাপ এবং আরও সক্রিয় থ্রেডগুলিতে আপনার আরও প্রসঙ্গের স্যুইচগুলি ঘটতে চলেছে। কয়েক বছর আগে আমি আমার পিসিতে একটি সি # কনসোল অ্যাপ্লিকেশন এবং প্রায় দিয়ে একটি পরীক্ষা চালিয়েছি। 500 থ্রেডগুলি আমার সিপিইউ 100% ছিল, থ্রেডগুলি উল্লেখযোগ্য কিছু করছে না। নেটওয়ার্ক কমগুলির জন্য থ্রেডের সংখ্যাটি নিচে রাখাই ভাল।
সিপুইজ

1
আমি হয় কোনও টাস্ক সলিউশন নিয়ে যাব বা এসাইক / অপেক্ষারত ব্যবহার করব। কার্য সমাধানটি সহজ বলে মনে হচ্ছে যখন অ্যাসিঙ্ক / অপেক্ষার সম্ভাবনাটি আরও বেশি পরিমাণে স্কেলযোগ্য (এগুলি বিশেষত আইও-আবদ্ধ পরিস্থিতির জন্য বোঝানো হয়েছিল)।
মার্সেল পপেস্কু

5

.NET- র সমন্বিত Async IO ( BeginReadইত্যাদি) ব্যবহার করা ভাল ধারণা যদি আপনি সমস্ত বিবরণ সঠিকভাবে পেতে পারেন। যখন আপনি সঠিকভাবে আপনার সকেট / ফাইল হ্যান্ডলগুলি সেট আপ করবেন এটি কোনও ও থ্রেড ব্যবহার না করেই আপনার ক্রিয়াকলাপগুলি সম্পূর্ণ করতে দেয় (বা সবচেয়ে খারাপ ক্ষেত্রে, কোনও থ্রেড যা আমি বিশ্বাস করি তার পরিবর্তে কার্নেলের আইও থ্রেড পুল থেকে আসে) .NET এর থ্রেড পুলের, যা থ্রেডপুলের ভিড় দূর করতে সহায়তা করে))

প্রধান গোটচা হ'ল আপনি আপনার সকেট / ফাইলগুলি অ-ব্লকিং মোডে খোলেন তা নিশ্চিত করা। বেশিরভাগ ডিফল্ট সুবিধার ফাংশন (যেমন File.OpenRead) এটি করেন না, তাই আপনাকে নিজের লেখা দরকার write

অন্য প্রধান উদ্বেগগুলির মধ্যে একটি হ'ল ত্রুটি পরিচালনা করা - সঠিকভাবে তাত্পর্যপূর্ণ I / O কোড লেখার সময় ত্রুটিগুলি পরিচালনা করা অনেক বেশি, সিঙ্ক্রোনাস কোডে করা থেকে অনেক বেশি শক্ত। আপনি সরাসরি থ্রেড ব্যবহার নাও করতে পারলেও জাতি শর্ত এবং ডেডলকগুলি সহ শেষ হওয়া খুব সহজ very তাই আপনার এ সম্পর্কে সচেতন হওয়া প্রয়োজন।

যদি সম্ভব হয় তবে আপনার স্কেলযোগ্য অ্যাসিনক্রোনাস আইও করার প্রক্রিয়াটি সহজ করার জন্য কোনও সুবিধাগুলি গ্রন্থাগার ব্যবহার করা উচিত।

মাইক্রোসফ্টের কনকুরেন্সি সমন্বয় রানটাইম হ'ল এই জাতীয় প্রোগ্রামিংয়ের অসুবিধা হ্রাস করার জন্য ডিজাইন করা একটি। নেট গ্রন্থাগারের একটি উদাহরণ। এটি দুর্দান্ত দেখায়, তবে আমি এটি ব্যবহার না করায় এটি কতটা স্কেল হবে তা নিয়ে আমি মন্তব্য করতে পারি না।

আমার ব্যক্তিগত প্রকল্পগুলির জন্য যেগুলি অ্যাসিঙ্ক্রোনাস নেটওয়ার্ক বা ডিস্ক আই / ও করা দরকার, আমি স্কট্রেড.টাস্ক নামে একটি বিগত বছর ধরে তৈরি করে .NET সম্মতি / আইও সরঞ্জামগুলির একটি সেট ব্যবহার করি । এটি imvu.task এবং মোচড়ের মতো লাইব্রেরি দ্বারা অনুপ্রাণিত হয়েছে এবং আমি সংগ্রহস্থলে কিছু কার্যকারী উদাহরণ অন্তর্ভুক্ত করেছি যা নেটওয়ার্ক I / O করে। আমি লিখেছি এমন কয়েকটি অ্যাপ্লিকেশনগুলিতেও এটি ব্যবহার করেছি - বৃহত্তম প্রকাশ্যে প্রকাশিত একজন এনডিএক্সার (যা এটি থ্রেডলেস ডিস্ক আই / ও এর জন্য ব্যবহার করে)। গ্রন্থাগারটি imvu.task এর সাথে আমার অভিজ্ঞতার ভিত্তিতে লেখা হয়েছিল এবং মোটামুটি বিস্তৃত ইউনিটের পরীক্ষার সেট রয়েছে, তাই এটি চেষ্টা করার জন্য আমি দৃ strongly়ভাবে আপনাকে উত্সাহিত করি। এটি নিয়ে আপনার যদি কোনও সমস্যা থাকে তবে আমি আপনাকে কিছু সহায়তা দেওয়ার জন্য খুশি হব।

আমার মতে, থ্রেডের পরিবর্তে অ্যাসিনক্রোনাস / থ্রেডলেস আইও ব্যবহার করা আমার অভিজ্ঞতার ভিত্তিতে। নেট প্ল্যাটফর্মে যতক্ষণ আপনি শেখার বক্ররেখার সাথে মোকাবিলা করার জন্য প্রস্তুত থাকেন ততক্ষণ সার্থক প্রচেষ্টা। এটি আপনাকে থ্রেড অবজেক্টের ব্যয়ের দ্বারা আরোপিত স্কেল্যাবিলিটি ঝামেলাগুলি এড়াতে সহায়তা করে এবং অনেক ক্ষেত্রে ফিউচার / প্রতিশ্রুতির মতো সম্মতিযুক্ত আদিমাগুলি যত্ন সহকারে ব্যবহার করে আপনি লক এবং মুটেক্সের ব্যবহার সম্পূর্ণভাবে এড়াতে পারবেন।


দুর্দান্ত তথ্য, আমি আপনার উল্লেখগুলি যাচাই করে নেব এবং কী তা বুঝবে তা বোঝাব।
এরিক ফানকেনবাশ

3

আমি কেভিনের সমাধানটি ব্যবহার করেছি তবে সে বলেছে যে সমাধানে বার্তাগুলি পুনরায় ছাড়ার কোড নেই। বিকাশকারীগণ বার্তাগুলি পুনরায় ছড়িয়ে দেওয়ার জন্য এই কোডটি ব্যবহার করতে পারেন:

private static void ReceiveCallback(IAsyncResult asyncResult )
{
    ClientInfo cInfo = (ClientInfo)asyncResult.AsyncState;

    cInfo.BytesReceived += cInfo.Soket.EndReceive(asyncResult);
    if (cInfo.RcvBuffer == null)
    {
        // First 2 byte is lenght
        if (cInfo.BytesReceived >= 2)
        {
            //this calculation depends on format which your client use for lenght info
            byte[] len = new byte[ 2 ] ;
            len[0] = cInfo.LengthBuffer[1];
            len[1] = cInfo.LengthBuffer[0];
            UInt16 length = BitConverter.ToUInt16( len , 0);

            // buffering and nulling is very important
            cInfo.RcvBuffer = new byte[length];
            cInfo.BytesReceived = 0;

        }
    }
    else
    {
        if (cInfo.BytesReceived == cInfo.RcvBuffer.Length)
        {
             //Put your code here, use bytes comes from  "cInfo.RcvBuffer"

             //Send Response but don't use async send , otherwise your code will not work ( RcvBuffer will be null prematurely and it will ruin your code)

            int sendLenghts = cInfo.Soket.Send( sendBack, sendBack.Length, SocketFlags.None);

            // buffering and nulling is very important
            //Important , set RcvBuffer to null because code will decide to get data or 2 bte lenght according to RcvBuffer's value(null or initialized)
            cInfo.RcvBuffer = null;
            cInfo.BytesReceived = 0;
        }
    }

    ContinueReading(cInfo);
 }

private static void ContinueReading(ClientInfo cInfo)
{
    try 
    {
        if (cInfo.RcvBuffer != null)
        {
            cInfo.Soket.BeginReceive(cInfo.RcvBuffer, cInfo.BytesReceived, cInfo.RcvBuffer.Length - cInfo.BytesReceived, SocketFlags.None, ReceiveCallback, cInfo);
        }
        else
        {
            cInfo.Soket.BeginReceive(cInfo.LengthBuffer, cInfo.BytesReceived, cInfo.LengthBuffer.Length - cInfo.BytesReceived, SocketFlags.None, ReceiveCallback, cInfo);
        }
    }
    catch (SocketException se)
    {
        //Handle exception and  Close socket here, use your own code 
        return;
    }
    catch (Exception ex)
    {
        //Handle exception and  Close socket here, use your own code 
        return;
    }
}

class ClientInfo
{
    private const int BUFSIZE = 1024 ; // Max size of buffer , depends on solution  
    private const int BUFLENSIZE = 2; // lenght of lenght , depends on solution
    public int BytesReceived = 0 ;
    public byte[] RcvBuffer { get; set; }
    public byte[] LengthBuffer { get; set; }

    public Socket Soket { get; set; }

    public ClientInfo(Socket clntSock)
    {
        Soket = clntSock;
        RcvBuffer = null;
        LengthBuffer = new byte[ BUFLENSIZE ];
    }   

}

public static void AcceptCallback(IAsyncResult asyncResult)
{

    Socket servSock = (Socket)asyncResult.AsyncState;
    Socket clntSock = null;

    try
    {

        clntSock = servSock.EndAccept(asyncResult);

        ClientInfo cInfo = new ClientInfo(clntSock);

        Receive( cInfo );

    }
    catch (SocketException se)
    {
        clntSock.Close();
    }
}
private static void Receive(ClientInfo cInfo )
{
    try
    {
        if (cInfo.RcvBuffer == null)
        {
            cInfo.Soket.BeginReceive(cInfo.LengthBuffer, 0, 2, SocketFlags.None, ReceiveCallback, cInfo);

        }
        else
        {
            cInfo.Soket.BeginReceive(cInfo.RcvBuffer, 0, cInfo.BytesReceived, SocketFlags.None, ReceiveCallback, cInfo);

        }

    }
    catch (SocketException se)
    {
        return;
    }
    catch (Exception ex)
    {
        return;
    }

}


1

আপনি ACE (অ্যাডাপটিভ কমিউনিকেশনস এনভায়রনমেন্ট) নামক একটি কাঠামো ব্যবহার করার চেষ্টা করতে পারেন যা নেটওয়ার্ক সার্ভারগুলির জন্য জেনেরিক সি ++ ফ্রেমওয়ার্ক। এটি একটি অত্যন্ত শক্ত, পরিপক্ক পণ্য এবং উচ্চ-নির্ভরযোগ্যতা, উচ্চ-ভলিউম অ্যাপ্লিকেশনগুলি টেলকো-গ্রেড পর্যন্ত সমর্থন করার জন্য ডিজাইন করা হয়েছে।

কাঠামোটি বেশ কয়েকটি বিস্তৃত সমঝোতা মডেলগুলির সাথে সম্পর্কিত এবং সম্ভবত আপনার অ্যাপ্লিকেশনটির জন্য বাক্সের বাইরে উপযুক্ত। এর ফলে সিস্টেমটিকে ডিবাগ করা সহজ হয়ে যায় কারণ বেশিরভাগ বাজে ইচ্ছামত ইস্যু ইতিমধ্যে সাজানো হয়েছে। এখানে বাণিজ্য বন্ধ হ'ল ফ্রেমওয়ার্কটি সি ++ তে লিখিত এবং কোড বেসগুলির মধ্যে সবচেয়ে উষ্ণ এবং তুলতুলে নয়। অন্যদিকে, আপনি পরীক্ষিত হয়ে উঠেন, শিল্প গ্রেড নেটওয়ার্ক অবকাঠামো এবং বাক্সের বাইরে একটি অত্যন্ত স্কেলযোগ্য আর্কিটেকচার।


2
এটি একটি ভাল পরামর্শ, তবে আমি বিশ্বাস করি যে প্রশ্নগুলির ট্যাগগুলি থেকে ওপি সি #
জে.প.কোস্টা

আমি লক্ষ্য করেছি যে; পরামর্শটি ছিল যে এটি সি ++ এর জন্য উপলব্ধ এবং আমি সি # এর সমতুল্য কিছু সম্পর্কে অবগত নই। এই ধরণের সিস্টেমে ডিবাগ করা সেরা সময়ে সহজ নয় এবং আপনি এই কাঠামোয় যাওয়া থেকে ফিরে আসতে পারেন যদিও এর অর্থ সি ++ এ স্যুইচ করা।
কনসার্নড

হ্যাঁ, এটি সি # আমি ভাল। নেট ভিত্তিক সমাধান খুঁজছি। আমার আরও পরিষ্কার হওয়া উচিত ছিল, তবে আমি ধরে
নিয়েছিলাম

1

আমি SEDA বা একটি হালকা ওজনের থ্রেডিং গ্রন্থাগার ব্যবহার করব ( সার্ভারের দিকে এনআরটিএল স্কেলিবিলিটি দেখুন ইরং বা আরও নতুন লিনাক্স )। আপনার যোগাযোগটি যদি না হয় তবে অ্যাসিঙ্ক কোডিং খুব জটিল :)


1

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


1

আমি। নেট 3.5 তে যুক্ত হওয়া অ্যাকসেপ্টএসিঙ্ক / কানেক্টএন্সিঙ্ক / রিসিভএসিএনসি / সেন্ডএএনসিঙ্ক পদ্ধতিগুলি ব্যবহার করব। আমি একটি মাপদণ্ড তৈরি করেছি এবং তারা প্রায় 35% দ্রুত (প্রতিক্রিয়া সময় এবং বিটরেট) 100 জন ব্যবহারকারী ক্রমাগত ডেটা প্রেরণ এবং গ্রহণ করে।


1

লোকেদের গ্রহণযোগ্য উত্তরটি আটকানোর অনুলিপি করতে, আপনি _serverSket.BeginAccept (নতুন AsyncCallback (গ্রহণক্যালব্যাক), _serverSket) এর সমস্ত কলগুলি সরিয়ে গ্রহণযোগ্য কলব্যাক পদ্ধতিটি পুনরায় লিখতে পারেন; এবং এটিকে শেষ অবধি {{ধারাতে রেখে দিন:

private void acceptCallback(IAsyncResult result)
    {
       xConnection conn = new xConnection();
       try
       {
         //Finish accepting the connection
         System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
         conn = new xConnection();
         conn.socket = s.EndAccept(result);
         conn.buffer = new byte[_bufferSize];
         lock (_sockets)
         {
           _sockets.Add(conn);
         }
         //Queue recieving of data from the connection
         conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
       }
       catch (SocketException e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
       }
       catch (Exception e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
       }
       finally
       {
         //Queue the next accept, think this should be here, stop attacks based on killing the waiting listeners
         _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);       
       }
     }

আপনি এমনকি প্রথম ক্যাচটি মুছে ফেলতে পারেন কারণ এটির বিষয়বস্তু একই, তবে এটি একটি টেম্পলেট পদ্ধতি এবং ব্যতিক্রমগুলি ভালভাবে পরিচালনা করতে এবং ত্রুটির কারণ কী তা বোঝার জন্য আপনার টাইপ করা ব্যতিক্রম ব্যবহার করা উচিত, সুতরাং কিছু কার্যকর কোড সহ সেই ক্যাচগুলি কার্যকর করুন


0

আমি এসিইতে এই বইগুলি পড়ার পরামর্শ দেব

আপনাকে দক্ষ সার্ভার তৈরি করতে দেয় নিদর্শন সম্পর্কে ধারণা পেতে।

যদিও এসিই সি ++ এ প্রয়োগ করা হয়েছে তবে বইগুলি প্রচুর দরকারী নিদর্শনগুলি কভার করে যা কোনও প্রোগ্রামিং ভাষায় ব্যবহার করা যেতে পারে।


-1

পরিষ্কার হওয়ার জন্য, আমি। নেট ভিত্তিক সমাধানগুলি সন্ধান করছি (সম্ভব হলে সি #, তবে কোনও নেট ভাষা কাজ করবে)

আপনি নেট। নেট দিয়ে নিখুঁতভাবে চলে গেলে আপনি সর্বোচ্চ মাত্রার স্কেলাবিলিটি পাবেন না। জিসি বিরতি বিলম্বকে বাধা দিতে পারে।

আমি পরিষেবাটির জন্য কমপক্ষে একটি থ্রেড শুরু করতে চাই। আমি অ্যাসিঞ্চ এপিআই (বিগিনিক্রিভি, ইত্যাদি।) ব্যবহার করার বিষয়ে বিবেচনা করছি যেহেতু আমি জানি না যে আমি কোনও নির্দিষ্ট সময়ে (সম্ভবত কয়েকশ) কত সংখ্যক ক্লায়েন্ট সংযুক্ত হয়েছি। আমি অবশ্যই প্রতিটি সংযোগের জন্য কোনও থ্রেড শুরু করতে চাই না।

ওভারল্যাপড আইও সাধারণত নেটওয়ার্ক যোগাযোগের জন্য উইন্ডোজের দ্রুততম এপিআই হিসাবে বিবেচিত হয়। আমি জানি না এটি আপনার Asynch API এর মতো কিনা as প্রতিটি কলকে সক্রিয় সকেটে কলব্যাকের পরিবর্তে খোলা প্রতিটি সকেট যাচাই করা দরকার হিসাবে নির্বাচন করুন না।


1
আমি আপনার জিসি বিরতি মন্তব্য বুঝতে পারি না .. আমি স্কেলিবিলিটি সমস্যা এমন কোনও সিস্টেম দেখিনি যা সরাসরি জিসির সাথে সম্পর্কিত ছিল।
মার্কেট

4
এটি এমন অনেক বেশি সম্ভাবনা রয়েছে যে আপনি একটি অ্যাপ্লিকেশন তৈরি করেছেন যা জিসি বিদ্যমান থাকার চেয়ে দুর্বল আর্কিটেকচারের কারণে স্কেল করতে পারে না। নেট এবং জাভা উভয় দিয়েই বিশাল আকার পরিবর্তনযোগ্য + পারফরম্যান্ট সিস্টেম তৈরি করা হয়েছে। আপনি যে দুটি লিঙ্কই দিয়েছেন, উভয়টিতে কারণটি সরাসরি আবর্জনা সংগ্রহের নয় .. তবে গাদা সোয়াপিংয়ের সাথে সম্পর্কিত। আমি সন্দেহ করব যে এটি আর্কিটেকচারের ক্ষেত্রে সত্যিই এমন একটি সমস্যা যা এড়ানো যেত .. যদি আপনি আমাকে এমন কোনও ভাষা দেখাতে পারেন যে এমন একটি সিস্টেম তৈরি করা সম্ভব নয় যা স্কেল করতে পারে না, তবে আমি আনন্দের সাথে এটি ব্যবহার করব;)
মার্কেট

1
আমি এই মন্তব্যে একমত নই। অজানা, আপনি যে প্রশ্নগুলি উল্লেখ করেছেন তা হ'ল জাভা এবং সেগুলি বিশেষত বৃহত্তর মেমরির বরাদ্দ নিয়ে কাজ করছে এবং ম্যানুয়ালি জিসিকে জোর করার চেষ্টা করছে। আমি সত্যিই এখানে চলছে প্রচুর পরিমাণে মেমরি বরাদ্দ। এটি কেবল কোনও সমস্যা নয় not কিন্তু ধন্যবাদ. হ্যাঁ, অ্যাসিনক্রোনাস প্রোগ্রামিং মডেল সাধারণত ওভারল্যাপড আইও এর শীর্ষে প্রয়োগ করা হয়।
এরিক ফানকেনবাশ

1
আসলে, সেরা অনুশীলনটি নিয়মিতভাবে জিসিকে সংগ্রহ করতে বাধ্য করা হয় না to এটি আপনার অ্যাপ্লিকেশনটিকে আরও খারাপভাবে সম্পাদন করতে পারে। .NET জিসি একটি প্রজন্মের জিসি যা আপনার অ্যাপ্লিকেশনটির ব্যবহারের সাথে তাল মিলিয়ে দেবে। আপনি যদি সত্যিই ভাবেন যে আপনাকে ম্যানুয়ালি জিসি.কলেক্ট কল করতে হবে, আমি বলব যে আপনার কোডটি সম্ভবত অন্যভাবে লেখা প্রয়োজন ..
মার্কেট

1
@ মার্কেট, এটি এমন লোকদের জন্য মন্তব্য যাঁরা সত্যই আবর্জনা সংগ্রহ সম্পর্কে কিছুই জানেন না। যদি আপনার অলস সময় থাকে তবে ম্যানুয়াল সংগ্রহ করাতে কোনও সমস্যা নেই। এটি শেষ হয়ে গেলে আপনার অ্যাপ্লিকেশনটিকে আরও খারাপ করে তুলবে না। একাডেমিক কাগজপত্রগুলি দেখায় যে প্রজন্মের জিসিগুলি কাজ করে কারণ এটি আপনার অবজেক্টগুলির আজীবন একটি আনুমানিক। অবশ্যই এটি নিখুঁত উপস্থাপনা নয়। আসলে, এখানে একটি প্যারাডাক্স রয়েছে যেখানে "প্রাচীনতম" প্রজন্মের প্রায়শই আবর্জনার সর্বাধিক অনুপাত থাকে কারণ এটি কখনও আবর্জনা সংগ্রহ করা হয় না।
অজানা

-1

আপনি উচ্চ-কর্মক্ষমতা সার্ভার বিকাশের জন্য পুশ ফ্রেমওয়ার্ক ওপেন সোর্স ফ্রেমওয়ার্ক ব্যবহার করতে পারেন। এটি আইওসিপি-তে নির্মিত এবং পুশ পরিস্থিতি এবং বার্তা সম্প্রচারের জন্য উপযুক্ত।

http://www.pushframework.com


1
এই পোস্টটি সি # এবং। নেট ট্যাগ ছিল। আপনি কেন একটি সি ++ কাঠামোর পরামর্শ দিয়েছেন?
এরিক ফানকেনবাশ

সম্ভবত কারণ তিনি এটি লিখেছিলেন। butterosoftware.com/…
কুইলব্রেকার

পুশফ্রেমওয়ার্ক একাধিক সার্ভারের দৃষ্টান্ত সমর্থন করে? যদি না হয়, কিভাবে এটি স্কেল হয়?
ইস্কর
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.