আমি এর আগেও এরকম কিছু লিখেছি। আমার গবেষণা থেকে কয়েক বছর আগে দেখানো হয়েছিল যে আপনার নিজের সকেট প্রয়োগটি অসিঙ্ক্রোনাস সকেট ব্যবহার করে সেরা বেট ছিল। এর অর্থ হ'ল ক্লায়েন্টরা আসলে কিছু না করে আসলে অপেক্ষাকৃত কম সংস্থান প্রয়োজন। যা ঘটে থাকে তা। নেট থ্রেড পুল দ্বারা পরিচালিত হয়।
আমি এটি ক্লাস হিসাবে লিখেছি যা সার্ভারগুলির জন্য সমস্ত সংযোগ পরিচালনা করে।
সমস্ত ক্লায়েন্ট সংযোগগুলি ধরে রাখতে আমি কেবল একটি তালিকা ব্যবহার করেছি, তবে বৃহত্তর তালিকার জন্য আপনার যদি দ্রুত অনুসন্ধানের প্রয়োজন হয় তবে আপনি এটি চাইলে লিখতে পারেন।
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
সময়ে সময়ে যে কোনও সময়ে এসেছি এবং থ্রেড সিঙ্ক্রোনাইজেশন ব্যবহার করার দরকার নেই। যাইহোক, আপনি যদি ডেটাটি টান দেওয়ার সাথে সাথে পরের প্রাপ্তিকে কল করার জন্য এটি পুনরায় অর্ডার করেন, যা কিছুটা দ্রুত হতে পারে, আপনাকে থ্রেডগুলি সঠিকভাবে সিঙ্ক্রোনাইজ করতে হবে তা নিশ্চিত করতে হবে।
এছাড়াও, আমি আমার কোডটি প্রচুর হ্যাক করে দিয়েছি, তবে যা ঘটছে তার সারাংশ রেখেছি। আপনার ডিজাইনের জন্য এটি একটি ভাল শুরু হওয়া উচিত। এই সম্পর্কে আপনার যদি আরও কোনও প্রশ্ন থাকে তবে একটি মন্তব্য করুন।