Zoeken…


Invoering

Door asynchrone sockets te gebruiken, kan een server luisteren naar inkomende verbindingen en in de tussentijd een andere logica uitvoeren, in tegenstelling tot synchrone socket wanneer ze luisteren, blokkeren ze de hoofdthread en reageert de toepassing niet meer en loopt een test vast totdat een client verbinding maakt.

Opmerkingen

Socket en netwerk

Hoe toegang te krijgen tot een server buiten mijn eigen netwerk? Dit is een veel voorkomende vraag en wanneer deze wordt gesteld, wordt deze meestal als onderwerp gemarkeerd.

Serverzijde

Op het netwerk van uw server moet u uw router doorsturen naar uw server.

Voor bijvoorbeeld een pc waarop de server actief is:

lokaal IP = 192.168.1.115

Server luistert naar poort 1234.

Stuur binnenkomende verbindingen op Port 1234 router door naar 192.168.1.115

Kant van de cliënt

Het enige dat u hoeft te veranderen is het IP. U wilt geen verbinding maken met uw loopback-adres maar met het openbare IP-adres van het netwerk waarop uw server actief is. Dit IP kunt u hier krijgen.

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

Dus nu maakt u een aanvraag voor dit eindpunt: 10.10.10.10:1234 als u uw poort doorstuurt naar uw router, zullen uw server en client probleemloos verbinding maken.

Als je verbinding wilt maken met een lokaal IP-adres, hoef je niet te portforwart, verander het loopback-adres in 192.168.1.178 of iets dergelijks.

Gegevens verzenden:

Gegevens worden in byte-array verzonden. U moet uw gegevens in een byte-array verpakken en aan de andere kant uitpakken.

Als u bekend bent met socket, kunt u ook proberen uw byte-array te coderen voordat u deze verzendt. Dit voorkomt dat iemand uw pakket steelt.

Voorbeeld van asynchrone socket (client / server).

Server Side voorbeeld

Maak luisteraar voor server

Begin met het maken van een server die clients behandelt die verbinding maken en aanvragen die worden verzonden. Dus maak een luisteraar klasse die dit aankan.

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

Eerst moeten we de luisteraar-socket initialiseren waar we kunnen luisteren naar eventuele verbindingen. We gaan een Tcp-socket gebruiken, daarom gebruiken we SocketType.Stream. We specificeren ook naar welke poort de server moet luisteren

Daarna beginnen we te luisteren naar inkomende verbindingen.

De boommethoden die we hier gebruiken zijn:

  1. ListenerSocket.Bind ();

    Deze methode bindt de socket aan een IPEndPoint . Deze klasse bevat de host- en lokale of externe poortinformatie die een toepassing nodig heeft om verbinding te maken met een service op een host.

  2. ListenerSocket.Listen (10);

    De parameter achterstand geeft het aantal binnenkomende verbindingen aan dat in de wachtrij kan worden geplaatst voor acceptatie.

  3. ListenerSocket.BeginAccept ();

    De server begint te luisteren naar inkomende verbindingen en gaat door met andere logica. Wanneer er een verbinding is, schakelt de server terug naar deze methode en voert de methode AcceptCallBack uit

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

Dus wanneer een client verbinding maakt, kunnen we deze als volgt accepteren:

Drie methoden die we hier gebruiken zijn:

  1. ListenerSocket.EndAccept ()

    We zijn begonnen met het terugbellen met Listener.BeginAccept() nu moeten we dat terugbellen beëindigen. The EndAccept() methode The EndAccept() accepteert een parameter IAsyncResult. Hiermee wordt de status van de asynchrone methode opgeslagen. Vanuit deze status kunnen we de socket extraheren waar de binnenkomende verbinding vandaan kwam.

  2. ClientController.AddClient()

    Met de socket die we van EndAccept() hebben gekregen, maken we een client met een eigen methode (code ClientController onder servervoorbeeld) .

  3. ListenerSocket.BeginAccept ()

    We moeten opnieuw beginnen met luisteren wanneer de socket klaar is met het verwerken van de nieuwe verbinding. Geef de methode door die deze callback zal vangen. En geef ook de Listener-socket door, zodat we deze socket voor toekomstige verbindingen kunnen hergebruiken.

    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 hebben we een Listening Socket, maar hoe ontvangen we gegevens die door de client zijn verzonden, dat is wat de volgende code toont.

Maak een serverontvanger voor elke client

Maak eerst een ontvangstklasse met een constructor die een Socket als parameter opneemt:

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

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

In de volgende methode beginnen we eerst met het geven van de buffer een grootte van 4 bytes (Int32) of pakket bevat onderdelen {lengte, werkelijke gegevens}. Dus de eerste 4 bytes reserveren we voor de lengte van de gegevens, de rest voor de werkelijke gegevens.

Vervolgens gebruiken we de methode BeginReceive () . Deze methode wordt gebruikt om te beginnen met het ontvangen van verbonden clients en wanneer gegevens worden ontvangen, wordt de functie 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()
    }

Daarom hebben we een server ingesteld die inkomende verbindingen kan ontvangen en beluisteren. Wanneer een client verbinding maakt, wordt deze toegevoegd aan een lijst met clients en heeft elke client zijn eigen ontvangstklasse. Om de server te laten luisteren:

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

Sommige klassen gebruik ik in dit voorbeeld

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

Voorbeeld clientzijde

Verbinden met de server

Allereerst willen we een klasse maken die verbinding maakt met de server die we de naam geven: Connector:

class Connector
{
    private Socket _connectingSocket;
}

Volgende methode voor deze klasse is TryToConnect ()

Deze methode heeft enkele interessante dingen:

  1. Maak de socket;

  2. Vervolgens loop ik totdat de socket is aangesloten

  3. Elke lus die het slechts 1 seconde vasthoudt, willen we de server XD niet DOSEREN

  4. Met Connect () probeert het verbinding te maken met de server. Als het mislukt, genereert het een uitzondering, maar houdt het programma verbinding met de server. Je kunt hiervoor een Connect CallBack- methode gebruiken, maar ik ga gewoon een methode aanroepen wanneer de Socket is verbonden.

  5. Merk op dat de client nu probeert verbinding te maken met uw lokale pc op poort 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();
     }
    

Een bericht naar de server verzenden

Dus nu hebben we een bijna voltooide of Socket-applicatie. Het enige dat we niet hebben is een klasse voor het verzenden van een bericht naar de server.

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

Klik ten slotte op twee knoppen, één voor verbinden en de andere voor het verzenden van een bericht:

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

De clientklasse die ik in dit voorbeeld heb gebruikt

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

Merk op

De klasse Ontvangen van de server is dezelfde als de klasse Ontvangen van de client.

Conclusie

U hebt nu een server en een client. U kunt dit basisvoorbeeld uitwerken. Zorg er bijvoorbeeld voor dat de server ook bestanden of andere tings kan ontvangen. Of stuur een bericht naar de klant. Op de server heb je een lijst met clients, dus als je iets ontvangt waarvan je weet dat het bij de client vandaan komt.

Eindresultaat: voer hier de afbeeldingsbeschrijving in



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow