25 Kas 2015

C#'da Multi Client Server Programlama

Multi client server adından da anlaşılabileceği gibi sunucudan(server) istediği zaman bilgi isteyebilen ve bu bilgileri kullanıp üzerinde işlem yapan istemci(client) sayısının birden çok olması durumudur.

Sunucu programları bazı engellemelere rağmen birçok istemci ile bağlantı sağlayabilir. Aşağıdaki kodu inceleyin;


serversock.Bind(ipep); //ipep sunucunun bitiş noktasıdır
serversock.Listen(10);Socket client = serversock.Accept();


Bu 3 satır kodun uygulanmasından sonra, sunucu bağlanmak için bir istemci bekler. Bağlantı kurulduktan sonra bu istemci soketi kapanana kadar diğer istemciler sunucuya erişemez. Yani bu bir seferde sadece bir istemci ile çalışmak anlamına gelir.  İstemciler de bir seferde sadece bir kere bağlanabilirler. Bu yüzden istemciler sunucu kullanacakları çoğu zaman bu büyük bir sorun haline gelmektedir.

Günümüzde genellikle daha çok tercih edilen sunucu birçok istemciye aynı anda bağlantı sağlayan sunucu tipidir. Bunun için 2 yaklaşım kullanılabilir. Bunlar;
·         Poll( )/Select( ) Metodu
·         Çok Parçacıklı (Multithreaded) Server

1-     Poll( ) / Select( ) Metodu

Pool( )Metodu: .Net soket kütüphanesi, Unix soket kütüphanesinde bulunan ve engellenemeyen soket yöntemlerini içerir. Bu sayede programcılar Windows ortamında Unix ağ programlarına kolayca giriş sağlarlar. Poll( )/Select( ) metodunun formatı aşağıdaki gibidir; 
bool Poll(int microseconds, SelectMode mode); 
Eğer eylem tamamlanırsa true, eylem gerçekleşmezse false döndürülür. microseconds Poll( ) metodunun belirtilen olaylar için bekleyeceği ve soketi izleyeceği süreyi belirtir. SelectMode ise izlemek için kullanılacak eylem türünü belirtir. SelectMode metoduna bağlı bazı fonksiyonların sonucunun true olma koşulları aşağıda verilmiştir.

SelectMode.SelectRead’in değeri  aşağıdaki şartlarda Poll( ) metodunun true döndürmesine sebep olur;
·         Eğer Accept( ) metodunun çağrısı başarılı olursa,
·         Eğer soket üzerinde veri varsa ve
·         Eğer bağlantı kapatıldı ise

SelectMode.SelectWrite’ın değerinin true döndürmesine sebep olacak koşullar ise aşağıdadır;
·         Eğer bir Connect( ) metodu çağrısı başarılı olursa
·         Ya da veri soket üzerinde gönderilebilirse

SelectMode.SelectError’ın değerinin true döndürme koşulları;
·         Bir Connect( ) metodu başarısız olduysa,
·         Ya da elimizde “out-of-band data” mevcut ise ve soketin OutOfBandInline özelliği henüz ayarlanmamış ise

Poll( ) Metoduna Dayalı Örnek Bir Sunucu Programı

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ConsoleApplication1
{
class tcpPollServer
{
static void Main(string[] args)
{
int recv;
byte[] data = new byte[1023];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsock.Bind(ipep);
newsock.Listen(10);
Console.WriteLine("Waiting for a client ...");
bool result;
int i = 0;
while (true)
{
i++;
Console.WriteLine("Polling for accept#{0}| ...", i);
result = newsock.Poll(500000, SelectMode.SelectRead); //0.5 second
if (result) break;
}
Socket client = newsock.Accept();
IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("Connected with {0} at port {1}", newclient.Address, newclient.Port); 
string welcome = "Welcome to Poll server";
data = Encoding.ASCII.GetBytes(welcome);
client.Send(data, data.Length, SocketFlags.None);
i = 0;
while (true)
{
i++;
result = client.Poll(3000000, SelectMode.SelectRead); //3 sec
if (result)
{
data = new byte[1024];
i = 0;
recv = client.Receive(data);
if (recv == 0) break;

Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
client.Send(data, recv, 0);
}
}

Console.WriteLine("Disconnected from {0}", newclient.Address);
client.Close();
newsock.Close();   
}     
}     
}


Select( ) Metodu: Select metodu bazı fonksiyonları engellemek için soketlerden bir ya da bir kaçını budar. Soketler okuma ve yazma için uygun hale geldikçe Select metodu hangilerinin kullanıma hazır olduğuna ve hangilerinin eğer kullanılırsa engellenebileceğine karar verir. Bu metodun formatı şöyledir;

Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microseconds)

Ilist nesnesi ayrı ayrı, indeks değeri ile ulaşılabilecek nesne koleksiyonunu temsil eder.

Select( ) Metoduna Dayalı Örnek Bir Sunucu Programı

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ConsoleApplication1
{
class tcpSelectServer
{
static void Main(string[] args)
{
ArrayList sockList = new ArrayList(2);
ArrayList copyList = new ArrayList(2);
Socket main = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
byte[] data = new byte[1024];
string stringData;
int recv;
main.Bind(iep);
main.Listen(2);
Console.WriteLine("Waiting for 2 clients...");
Socket client1 = main.Accept();
IPEndPoint iep1 = (IPEndPoint)client1.RemoteEndPoint;
client1.Send(Encoding.ASCII.GetBytes("Selcome to Select server"));
Console.WriteLine("Connected to {0}", iep1.ToString());
sockList.Add(client1);
Console.WriteLine("Waiting for 1 more clients...");
Socket client2 = main.Accept();
IPEndPoint iep2 = (IPEndPoint)client2.RemoteEndPoint;
client2.Send(Encoding.ASCII.GetBytes("Selcome to Select server"));
Console.WriteLine("Connected to {0}", iep2.ToString());
sockList.Add(client2);
main.Close();
while (true)
{
copyList = new ArrayList(sockList);
Console.WriteLine("Monitoring {0} sockets...", copyList.Count);
Socket.Select(copyList, null, null, 1000000);

foreach(Socket client in copyList)
{
data = new byte[1024];
recv = client.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine("Received: {0}", stringData);
if (recv == 0)
{
iep = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("Client {0} disconnected.", iep.ToString());
client.Close();
sockList.Remove(client);
if (sockList.Count == 0)
{
Console.WriteLine("Last client disconnected, bye");
return;
}     
}
else
client.Send(data, recv, SocketFlags.None);
}
}
}
}
}

Eğer sunucu soketi gerçek veriyi alırsa, standar Send( ) metodunu kullanarak kaynak istemciye geri yansıtır.

2-     Çok Parçacıklı (Multithreaded) Server

Select( ) ya da Poll( ) metodunu kullanmanın sakıncası kodla sarılı hale gelen birçok istemciyi idare etmektir. Programcılık mantığı açısından, farklı aralıklardan veri gönderen ve alan birçok istemciyi bağdaştırmak oldukça zordur. Ayrıca bu yaklaşımların etkisiyle zaman içinde paylaşılan engelleme yöntemleri ve istemcilerin sırasını beklemesi gerekir. Her istemci sunucuyla her zaman konuşabilmesi için özel bir kanala sahip olsaydı daha iyi olabilirdi. İşte Multithread bu işe yarar.

Multithreaded Server’da kilit konu, ana programda ana sunucu Soket nesnesi oluşturmaktır. Her istemci sunucuya bağlandığında, ana program bağalntıyı sağlamak için ayrı bir iş parçacığı(multithread) yaratır.

Multithreaded Server Örnek Sunucu Programı

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class threadedServer
{
private TcpListener client;
public threadedServer()
{
client = new TcpListener(IPAddress.Any, 9050);
client.Start();
Console.WriteLine("Waiting for clients...");
while(true)
{
while(!client.Pending())
{
Thread.Sleep(1000);
}
ConnectionThread newconnection = new ConnectionThread();
newconnection.threadListner = this.client;
Thread newthread = new Thread( new ThreadStart(newconnection.HandleConnection));
newthread.Start();
}
}
public static void Main()
{
threadedServer server = new threadedServer();
}
}
class ConnectionThread
{
public TcpListener threadListner;
private static int connections =0;
public void HandleConnection()
{
int recv;
byte[] data = new byte[1024];
TcpClient client = threadListner.AcceptTcpClient();
NetworkStream ns = client.GetStream();
connections++;
Console.WriteLine("New client accepted: {0} active connections", connections);
string welcome = "Welcome to multithreaded server";
data = Encoding.ASCII.GetBytes(welcome);
ns.Write(data, 0, data.Length);
while(true)
{
data = new byte[1024];
recv = ns.Read(data, 0 , data.Length);
if (recv == 0) break;
ns.Write(data, 0, recv);
}
ns.Close();
client.Close();
Console.WriteLine("Client disconnected: {0} active connections", connections);
}
}
}




Bu konudaki ilave bilgiler için mutlaka ThreadPool class’ı incelenmelidir.

Kaynak: “C# Network Programming,” by Richard Blum, Sybex.’dir.

Hiç yorum yok:

Yorum Gönder