Sök…


Introduktion

Genom att använda asynkrona uttag kan en server lyssna på inkommande anslutningar och göra någon annan logik under tiden i motsats till synkronuttag när de lyssnar blockerar de huvudtråden och applikationen blir svarsfri och kommer att frysa tills en klient ansluter.

Anmärkningar

Socket och nätverk

Hur kommer jag åt en server utanför mitt eget nätverk? Detta är en vanlig fråga och när den ställs flaggas det mest av ämnet.

Server sida

I ditt serverns nätverk måste du vidarebefordra din router till din server.

Till exempel PC där servern körs på:

lokal IP = 192.168.1.115

Servern lyssnar på port 1234.

Vidarebefordra inkommande anslutningar på Port 1234 routern till 192.168.1.115

Klientsidan

Det enda du behöver ändra är IP. Du vill inte ansluta till din loopback-adress utan till den offentliga IP-adressen från det nätverk som din server kör på. Denna IP kan du få här .

 _connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 1234));

Så nu skapar du en begäran på den här slutpunkten: 10.10.10.10:1234 om du gjorde 10.10.10.10:1234 vidarebefordrade din router kommer din server och klient att ansluta utan problem.

Om du vill ansluta till en lokal IP kommer du inte att behöva portvänta bara ändra loopback-adressen till 192.168.1.178 eller något liknande.

Skickar data:

Data skickas i byte-array. Du måste packa dina data i en byte-grupp och packa upp dem på andra sidan.

Om du känner till socket kan du också försöka kryptera din byte-grupp innan du skickar. Detta förhindrar att någon stjäl ditt paket.

Exempel på Asynchronous Socket (Client / Server).

Exempel på serversidan

Skapa lyssnare för servern

Börja med att skapa en server som hanterar klienter som ansluter och begäranden som kommer att skickas. Så skapa en lyssnarklass som hanterar detta.

class Listener
{
    public Socket ListenerSocket; //This is the socket that will listen to any incoming connections
    public short Port = 1234; // on this port we will listen

    public Listener()
    {
        ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    }
 }

Först måste vi initiera lyssnaruttaget där vi kan lyssna på för alla anslutningar. Vi kommer att använda ett Tcp-uttag och det är därför vi använder SocketType.Stream. Vi specificerar också för att häxa port servern ska lyssna på

Sedan börjar vi lyssna efter inkommande anslutningar.

Trädmetoderna vi använder här är:

  1. ListenerSocket.Bind ();

    Den här metoden binder kontakten till en IPEndPoint . Den här klassen innehåller information om värd och lokal eller fjärrport som en applikation behöver för att ansluta till en tjänst på en värd.

  2. ListenerSocket.Listen (10);

    Parametern för backlog anger antalet inkommande anslutningar som kan stå i kö för godkännande.

  3. ListenerSocket.BeginAccept ();

    Servern börjar lyssna efter inkommande anslutningar och fortsätter med annan logik. När det finns en anslutning växlar servern tillbaka till den här metoden och kör AcceptCallBack-metoden

    public void StartListening()
    {
        try
        {                
                MessageBox.Show($"Listening started port:{Port} protocol type: {ProtocolType.Tcp}");                    
                ListenerSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
                ListenerSocket.Listen(10);
                ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);                
        }
        catch(Exception ex)
        {
            throw new Exception("listening error" + ex);
        }
    }

Så när en klient ansluter kan vi acceptera dem med den här metoden:

Tre metoder som vi använder här är:

  1. ListenerSocket.EndAccept ()

    Vi startade återuppringningen med Listener.BeginAccept() slutar nu måste vi avsluta det samtalet igen. The EndAccept() accepterar en IAsyncResult-parameter, detta lagrar tillståndet för den asynkrona metoden. Från det här läget kan vi extrahera uttaget där den inkommande anslutningen kom från.

  2. ClientController.AddClient()

    Med det uttag vi fick från EndAccept() skapar vi en klient med en egengjord metod (kod ClientController nedanför serverexempel) .

  3. ListenerSocket.BeginAccept ()

    Vi måste börja lyssna igen när uttaget är klart med hanteringen av den nya anslutningen. Ange metoden som kommer att fånga detta återuppringning. Och passera också lyssnaruttaget så att vi kan återanvända detta uttag för kommande anslutningar.

    public void AcceptCallback(IAsyncResult ar)
    {
        try
        {
            Console.WriteLine($"Accept CallBack port:{Port} protocol type: {ProtocolType.Tcp}");
            Socket acceptedSocket = ListenerSocket.EndAccept(ar);               
            ClientController.AddClient(acceptedSocket);

            ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
        }
        catch (Exception ex)
        {
            throw new Exception("Base Accept error"+ ex);
        }
    }

Nu har vi ett lyssningsuttag men hur får vi datasändning av klienten som är vad nästa kod visar.

Skapa servermottagare för varje klient

Skapa först en mottagningsklass med en konstruktör som tar in en socket som parameter:

    public class ReceivePacket
    {
        private byte[] _buffer;
        private Socket _receiveSocket;

        public ReceivePacket(Socket receiveSocket)
        {
           _receiveSocket = receiveSocket;
        }
    }

I nästa metod börjar vi först med att ge bufferten en storlek på 4 byte (Int32) eller paketet innehåller delar {längd, faktisk data}. Så de första 4 byte vi reserverar för längden på data resten för faktiska data.

Därefter använder vi metoden BeginReceive () . Den här metoden används för att börja ta emot från anslutna klienter och när den kommer att ta emot data kommer den att ReceiveCallback funktionen ReceiveCallback .

    public void StartReceiving()
    {
        try
        {
            _buffer = new byte[4];
            _receiveSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
        }
        catch {}
    }

    private void ReceiveCallback(IAsyncResult AR)
    {
        try
        {
            // if bytes are less than 1 takes place when a client disconnect from the server.
            // So we run the Disconnect function on the current client
            if (_receiveSocket.EndReceive(AR) > 1)
            {
                // Convert the first 4 bytes (int 32) that we received and convert it to an Int32 (this is the size for the coming data).
                _buffer = new byte[BitConverter.ToInt32(_buffer, 0)];  
                // Next receive this data into the buffer with size that we did receive before
                _receiveSocket.Receive(_buffer, _buffer.Length, SocketFlags.None); 
                // When we received everything its onto you to convert it into the data that you've send.
                // For example string, int etc... in this example I only use the implementation for sending and receiving a string.

                // Convert the bytes to string and output it in a message box
                string data = Encoding.Default.GetString(_buffer);
                MessageBox.Show(data);
                // Now we have to start all over again with waiting for a data to come from the socket.
                StartReceiving();
            }
            else
            {
                Disconnect();
            }
        }
        catch
        {
            // if exeption is throw check if socket is connected because than you can startreive again else Dissconect
            if (!_receiveSocket.Connected)
            {
                Disconnect();
            }
            else
            {
                StartReceiving();
            }
        }
    }

    private void Disconnect()
    {
        // Close connection
        _receiveSocket.Disconnect(true);
        // Next line only apply for the server side receive
        ClientController.RemoveClient(_clientId);
        // Next line only apply on the Client Side receive
        Here you want to run the method TryToConnect()
    }

Så vi har konfigurerat en server som kan ta emot och lyssna på inkommande anslutningar. När en klient ansluter kommer den att läggas till i en lista över klienter och varje klient har sin egen mottagningsklass. För att få servern att lyssna:

Listener listener = new Listener();
listener.StartListening();

Vissa klasser använder jag i det här exemplet

    class Client
    {
        public Socket _socket { get; set; }
        public ReceivePacket Receive { get; set; }
        public int Id { get; set; }

        public Client(Socket socket, int id)
        {
            Receive = new ReceivePacket(socket, id);
            Receive.StartReceiving();
            _socket = socket;
            Id = id;
        }
    }

     static class ClientController
     {
          public static List<Client> Clients = new List<Client>();

          public static void AddClient(Socket socket)
          {
              Clients.Add(new Client(socket,Clients.Count));
          }

          public static void RemoveClient(int id)
          {
              Clients.RemoveAt(Clients.FindIndex(x => x.Id == id));
          }
      }

Exempel på klientsidan

Ansluta till servern

Först och främst vill vi skapa en klass som ansluter till servern som vi ger det är: Connector:

class Connector
{
    private Socket _connectingSocket;
}

Nästa metod för denna klass är TryToConnect ()

Den här metoden får några intressanta saker:

  1. Skapa uttaget;

  2. Därefter slingrar jag tills uttaget är anslutet

  3. Varje slinga är det bara att hålla tråden i 1 sekund, vi vill inte DOS-servern XD

  4. Med Connect () kommer det att försöka ansluta till servern. Om den misslyckas kommer det att kasta ett undantag men wile kommer att hålla programmet anslutet till servern. Du kan använda en Connect CallBack- metod för det här, men jag ska bara ringa för att ringa en metod när Socket är ansluten.

  5. Observera att klienten nu försöker ansluta till din lokala dator på port 1234.

     public void TryToConnect()
     {
         _connectingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         
          while (!_connectingSocket.Connected)
          {
              Thread.Sleep(1000);
    
              try
              {
                  _connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
              }
              catch { }
          }
          SetupForReceiveing();
         }
     }
    
     private void SetupForReceiveing()
     {
        // View Client Class bottom of Client Example
         Client.SetClient(_connectingSocket);
         Client.StartReceiving();
     }
    

Skickar ett meddelande till servern

Så nu har vi en nästan finish eller Socket-applikation. Det enda som vi inte har jet är en klass för att skicka ett meddelande till servern.

public class SendPacket
{
    private Socket _sendSocked;

    public SendPacket(Socket sendSocket)
    {
        _sendSocked = sendSocket;
    }

    public void Send(string data)
    {
        try
        {         
            /* what hapends here:
                 1. Create a list of bytes
                 2. Add the length of the string to the list.
                    So if this message arrives at the server we can easily read the length of the coming message.
                 3. Add the message(string) bytes
            */
  
            var fullPacket = new List<byte>();
            fullPacket.AddRange(BitConverter.GetBytes(data.Length));
            fullPacket.AddRange(Encoding.Default.GetBytes(data));

            /* Send the message to the server we are currently connected to.
            Or package stucture is {length of data 4 bytes (int32), actual data}*/
            _sendSocked.Send(fullPacket.ToArray());
        }
        catch (Exception ex)
        {
            throw new Exception();
        }
    }

Slutför låda två knappar en för anslutning och den andra för att skicka ett meddelande:

    private void ConnectClick(object sender, EventArgs e)
    {
        Connector tpp = new Connector();
        tpp.TryToConnect();
    }

    private void SendClick(object sender, EventArgs e)
    {
        Client.SendString("Test data from client");
    }

Klientklass som jag använde i det här exemplet

    public static void SetClient(Socket socket)
    {
        Id = 1;
        Socket = socket;
        Receive = new ReceivePacket(socket, Id);
        SendPacket = new SendPacket(socket);
    }

Lägga märke till

Mottagningsklassen från servern är densamma som mottagarklassen från klienten.

Slutsats

Du har nu en server och en klient. Du kan arbeta med detta grundläggande exempel. Till exempel gör det att servern också kan ta emot filer eller andra tings. Eller skicka ett meddelande till klienten. På servern fick du en lista över klienten så när du får något du kommer att veta från med klienten kom det från.

Slutresultat: ange bildbeskrivning här



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow