Поиск…


Вступление

Используя асинхронные сокеты, сервер может прослушивать входящие подключения и выполнять некоторую другую логику в среднем времени в отличие от синхронного сокета, когда они прослушивают, они блокируют основной поток, и приложение становится невосприимчивым, и он замерзнет, ​​пока клиент не подключится.

замечания

Розетка и сеть

Как получить доступ к серверу за пределами моей собственной сети? Это общий вопрос, и когда его спрашивают, в основном помечены как темы.

Серверная сторона

В сети вашего сервера вам необходимо перенаправить маршрутизатор на ваш сервер.

Для примера ПК, на котором работает сервер:

локальный IP = 192.168.1.115

Сервер прослушивает порт 1234.

Перенаправить входящие соединения на маршрутизаторе Port 1234 на 192.168.1.115

Сторона клиента

Единственное, что вам нужно изменить, это IP. Вы не хотите подключаться к вашему loopback-адресу, но к общедоступному IP-адресу из сети, на которой работает ваш сервер. Этот IP-адрес вы можете получить здесь .

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

Итак, теперь вы создаете запрос на эту конечную точку: 10.10.10.10:1234 если вы сделали порт свойств, перенаправляете ваш маршрутизатор, ваш сервер и клиент будут подключаться без каких-либо проблем.

Если вы хотите подключиться к локальному IP-адресу, вам не придется переносить portforwart, просто изменив адрес loopback на 192.168.1.178 или что-то в этом роде.

Отправка данных:

Данные отправляются в байтовый массив. Вам необходимо упаковать данные в массив байтов и распаковать их с другой стороны.

Если вы знакомы с сокетом, вы также можете попытаться зашифровать массив байтов перед отправкой. Это предотвратит кражу вашего пакета.

Пример асинхронного сокета (клиент / сервер).

Пример сервера

Создать прослушиватель для сервера

Начните с создания сервера, который будет обрабатывать клиентов, которые подключаются, и запросы, которые будут отправляться. Поэтому создайте класс Listener, который будет обрабатывать это.

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

Сначала нам нужно инициализировать сокет Listener, где мы можем прослушивать любые подключения. Мы собираемся использовать Tcp Socket, поэтому мы используем SocketType.Stream. Также мы указываем, что порт witch должен прослушивать

Затем мы начинаем слушать любые входящие соединения.

Используемые здесь древовидные методы:

  1. ListenerSocket.Bind ();

    Этот метод связывает сокет с IPEndPoint . Этот класс содержит информацию о хосте и локальном или удаленном порту, необходимую приложению для подключения к службе на хосте.

  2. ListenerSocket.Listen (10);

    Параметр backlog указывает количество входящих соединений, которые могут быть поставлены в очередь для принятия.

  3. ListenerSocket.BeginAccept ();

    Сервер начнет прослушивать входящие соединения и продолжит работу с другой логикой. Когда есть соединение, сервер переключается на этот метод и запускает метод AcceptCallBackt

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

Поэтому, когда клиент подключается, мы можем принять их с помощью этого метода:

Здесь используются три метода:

  1. ListenerSocket.EndAccept ()

    Мы начали обратный вызов с помощью Listener.BeginAccept() теперь мы должны завершить этот вызов. The EndAccept() принимает параметр IAsyncResult, это сохранит состояние асинхронного метода. Из этого состояния мы можем извлечь сокет, из которого поступало входящее соединение.

  2. ClientController.AddClient()

    С помощью сокета, который мы получили от EndAccept() мы создаем Клиент с собственным методом (код ClientController ниже примера сервера) .

  3. ListenerSocket.BeginAccept ()

    Нам нужно снова начать прослушивание, когда сокет будет обработан новым соединением. Передайте метод, который поймает этот обратный вызов. А также передайте int socket Listener, чтобы мы могли повторно использовать этот сокет для предстоящих подключений.

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

Теперь у нас есть Listening Socket, но как мы получаем передачу данных клиентом, что показывает следующий код.

Создавать серверный приемник для каждого клиента

Сначала создайте класс получения с конструктором, который принимает параметр Socket as:

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

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

В следующем методе мы сначала начинаем с предоставления буфера размером 4 байта (Int32) или пакета для частей {длина, фактические данные}. Таким образом, первые 4 байта резервируют для длины данных остальное для фактических данных.

Затем мы используем метод BeginReceive () . Этот метод используется для начала приема от подключенных клиентов, и когда он получит данные, он будет запускать функцию 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()
    }

Поэтому мы настроили сервер, который может принимать и слушать входящие соединения. Когда клиенты подключаются, он будет добавлен в список клиентов, и каждый клиент имеет свой собственный класс приема. Чтобы прослушать сервер:

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

Некоторые классы, которые я использую в этом примере

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

Пример стороны клиента

Подключение к серверу

Прежде всего, мы хотим создать класс, который соединяется с именем сервера, которое мы даем: Connector:

class Connector
{
    private Socket _connectingSocket;
}

Следующий метод для этого класса - TryToConnect ()

Этот метод немного интересует:

  1. Создайте сокет;

  2. Затем я контактирую до сокета

  3. В каждом цикле он просто удерживает Thread в течение 1 секунды, мы не хотим DOS сервера XD

  4. С Connect () он попытается подключиться к серверу. Если он терпит неудачу, он выдает исключение, но wile будет поддерживать соединение с сервером. Вы можете использовать метод Connect CallBack для этого, но я просто перейду к вызову метода при подключении Socket.

  5. Обратите внимание, что Клиент теперь пытается подключиться к вашему локальному компьютеру на порту 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();
     }
    

Отправка сообщения на сервер

Итак, теперь у нас есть почти законченное приложение или приложение Socket. Единственное, что у нас нет, это класс для отправки сообщения на сервер.

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

Завершить ящик двумя кнопками один для подключения, а другой для отправки сообщения:

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

Класс клиента, который я использовал в этом примере

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

уведомление

Класс приема с сервера совпадает с классом приема от клиента.

Заключение

Теперь у вас есть сервер и клиент. Вы можете использовать этот основной пример. Например, убедитесь, что сервер также может получать файлы или другие мелодии. Или отправьте сообщение клиенту. На сервере у вас есть список клиентов, поэтому, когда вы получаете что-то, о чем узнаете, с клиентом он пришел.

Конечный результат: введите описание изображения здесь



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow