SORU
15 Mayıs 2009, Cuma


Aşarsanız ne Tcp/Ip sunucu tabanlı yazmak

Ben tasarım aşamasında yazılı bir yeni bir Windows Servis uygulaması olarak kabul eder TCP/IP bağlantıları için uzun süren bağlantılar (yani bu değil gibi HTTP orada çok kısa bağlantıları, daha ziyade bir istemci bağlanır ve bağlı kaldığı için saat ya da gün ya da hafta).

Ağ mimarisi tasarımı için en iyi yolu için fikirler arıyorum. Bu hizmet için en az bir konu ile başlamak istiyorum. Kaç herhangi bir zaman (belki yüzlerce) bağlı olacak bilmiyorum beri Asynch API (BeginRecieve, vb.) kullanarak düşünüyorum. Ben kesinlikle her bağlantı için bir iplik başlamak istemiyorum.

Veri öncelikle benim sunucudan müşterilerine akışı olacak, ama bazı komutlar vesileyle müşterilerine gönderdi olacak. Bu öncelikle bir sunucu düzenli olarak durum veri müşterilerine gönderen bir izleme uygulaması.

Bu mümkün olduğunca metin oluşturmak için en iyi yolu herhangi bir öneriniz var mı? Temel iş akışı? Teşekkürler.

Açık olmak gerekirse, arıyorum .EDİT: (mümkünse C# ama herhangi bir .net tabanlı çözümler dil kullanılacak net)

KELLE NOT: ödül verilecek, daha basit bir cevap daha bekliyorum. Bir çözüm bir çalışma örnek gerekir, ya da bir şey için bir işaretçi olarak veya kısa bir örnekte-line indirmek olabilir. Ve olmalı .net ve Windows tabanlı (herhangi bir .dil kabul edilebilir net)

EDİT: iyi cevap veren herkese teşekkür etmek istiyorum. Ne yazık ki, sadece birini kabul edebilirim, ve iyi bilinen daha başlangıç-Bitiş yöntemi kabul etmeyi seçtim. Esac çözüm daha iyi olabilir, ama hala bu işe nasıl emin değilim Yeni sayılır.

Düşündüm bütün cevapları iyi upvoted var, sizin için daha fazlasını yapmak isterdim. Tekrar teşekkürler.

CEVAP
18 Mayıs 2009, PAZARTESİ


Bir şey geçmişte buna benzer yazdım. Kendi yuva uygulamanız yazma en iyi bahis olduğunu araştırmam yıl önce gösterdi, zaman Uyumsuz yuva kullanarak. Bu müşteri gerçekten hiçbir şey yapmamak aslında nispeten az kaynak gerekli anlamına geliyordu. Ortaya bir şey tarafından ele alınır .net iş parçacığı havuzu.

Sunucular için tüm bağlantıları yöneten bir sınıf olarak yazdım.

Ben sadece tüm istemci bağlantıları tutmak için bir liste kullanılır, ama eğer daha büyük listeler için daha hızlı aramaları gerekirse, siz nasıl isterseniz öyle yazabilirsiniz.

private List<xConnection> _sockets;

Ayrıca yuva aslında gelen bağlantılar için listenning gerekir.

private System.Net.Sockets.Socket _serverSocket;

Start yöntemi aslında sunucu soket başlar ve herhangi bir gelen bağlantılar için dinleme başlar.

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;
 }

Dediğim gibi not özel durum işleme kodu kötü görünüyor, ama nedeni vardı istisna bastırma kodu, böylece orada herhangi bir istisna olacağını bastırılmış ve geri dönüş false eğer bir yapılandırma seçeneği ayarla, ama istediğim kaldırmak için kısalık aşkına.

Bu _serverSocket.BeginAccept(new AsyncCallback(acceptCallback)), _serverSocket) yukarıda esas olarak ayarlar bir kullanıcı bağlandığında acceptCallback yöntemini çağırmak için sunucu soket. Bu yöntem çalışır .Eğer olursa bir çok işlem engelleme varsa, otomatik olarak ek bir çalışan iş parçacığı oluşturma işleyen Net havuzu,. Bu şekilde sunucu üzerinde herhangi bir yük işlemek gerekir.

    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);
       }
     }

Yukarıdaki kod aslında sadece bitmiş kabul bağlantısından geliyor, kuyruklar BeginReceive bir geri arama çalıştırılacak müşteri veri gönderir, sonra da sıra gelecek acceptCallback kabul edecek bir sonraki istemci bağlantısı geliyor.

BeginReceive yöntem çağrısı istemciden veri aldığında ne yuva söyler. BeginReceive için müşteri veri gönderdiğinde veri kopyalayacak olan bir bayt dizisi, vermek gerekir. ReceiveCallback yöntemi veri alma nasıl kullanacağımızı hangi adı alacak.

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);
       }
     }
   }
 }

EDİT: bu desen kodu: bu alanda söylemeyi unutmuşum

//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);

Genellikle ne yapardım, ne kodu istiyorsun, iletilere paketlerin yeniden yapmak, ve iş parçacığı havuzu iş olarak onları oluşturun. Bu şekilde müşteri, bir sonraki sokağın BeginReceive işleme kod çalıştığı her ne ise gecikmeli değil.

Geri tamamlandıktan sonuna kadar alırlar çağırarak veri soket okuma kabul. Bu tampon sağlanan alma işlevi başlar doldurur. Yorum bıraktığım yerde istediğin zaman, eğer Müşteri daha fazla veri gönderir ve geri tekrar çalışacak BeginReceive sonraki yöntem diyoruz. Şimdi burada müşteri veri, geri arama yalnızca iletinin bir parçası olarak adlandırılabilir almak gönderdiğinde gerçekten zor kısmı. Montaj çok çok karmaşık hale gelebilir. Benim kendi yöntemini kullandım ve özel bir iletişim kuralı bir nevi bunun için yarattı. Ben onu bıraktım, ama eğer istek olursa ekleyebilirim. Bu işleyici aslında şimdiye kadar yazdığım kod en karmaşık parçası oldu.

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;
 }

Yukarıda gönderme yöntemi aslında Send zaman uyumlu bir çağrı, mesaj boyutları ve benim uygulama çok iş parçacıklı doğası gereği iyi oldu Bu benim için kullanır. Eğer her müşteri göndermek istiyorsanız, sadece üzerinden _sockets Listesi çekmeye çalışacak.

Yukarıda başvurulan görmek xConnection sınıfı temelde bayt tampon vardır bir yuva için basit bir sarıcı ve benim uygulamada bazı ekstralar.

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

Ayrıca başvuru için buradayız **25 s hep birlikte değiller rahatsız edebiliyor beri vardır.

using System.Net.Sockets;

Bu çok yardımcı oldu, temiz kod olmayabilir, ama umarım işe yarar. Ayrıca değiştirme hakkında daha yorgun olmalısın hangi kod bazı nüansları vardır. Biri için, sadece BeginAccept bir tek herhangi bir zamanda aradı. Çok can sıkıcı bir vardı .yıllar önce olan bu etrafında net hata, detayları hatırlamıyorum.

Ayrıca, ReceiveCallback kod bir sonraki elimize sırası önce yuvasından alınan işliyoruz. Bu, tek bir yuva için, sadece aslında ReceiveCallback daha sonra zaman içinde herhangi bir noktada olduğumuzu, ve iş parçacığı eşitleme kullanmak için ihtiyacımız yok anlamına gelir. Eğer bu sonraki hemen biraz daha hızlı olabilir hangi verileri çektikten sonra çağrı almak için Sipariş ancak, eğer düzgün bir iş parçacığı eşitleme emin olmak gerekir.

Ayrıca, benim bir sürü kod girdim ama yerde ne özü kaldı. Bu tasarım senin için iyi bir başlangıç olabilir. Eğer bu konuda daha fazla sorunuz varsa, bir yorum bırakın.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Jonah Penna

    Jonah Penna

    11 EYLÜL 2005
  • Sorikan

    Sorikan

    3 ŞUBAT 2008
  • Virtual Riot

    Virtual Riot

    19 Mayıs 2011