С# TCP-сервер для нескольких клиентов (неизвестное количество прослушивающих клиентов)

Я пытаюсь написать небольшую программу, построенную из сервера и некоторых клиентов. сервер будет передавать / отправлять текстовое сообщение клиентам (клиенту не разрешено отправлять обратно на сервер, все клиенты должны получать одно и то же текстовое сообщение с сервера). Я видел здесь несколько вопросов по этому вопросу и по сети, но не нашел точного решения, которое я ищу. большинство решений заключалось в создании потока для каждого клиента. но давайте предположим, что я не всегда знаю, сколько клиентов будут слушать в определенное время. может быть 2, а может быть 30. Я знаю, что есть способ создать пул потоков и позволить .NET справиться с назначением потоков, но у меня никогда не было хорошей практики с этим, и мои знания о многопоточности никуда не годятся. на данный момент у меня есть простая программа Sever to Client, которая работает, как и ожидалось, но опять же, только с одним клиентом. так вот мой код:

Сервер:

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows;

namespace NetMessageServerWpf
{
    public partial class MainWindow : Window
    {
        TcpClient client;

        public MainWindow()
        {
            InitializeComponent();
            this.tcpListener = new TcpListener(IPAddress.Any, 3000);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
        }

        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            if(client == null || !client.Connected)
                client = this.tcpListener.AcceptTcpClient();
            msg = txtMessage.Text;
            SendTCP(client);
        }

        public TcpListener tcpListener;
        public Thread listenThread;

        private string msg;

        private void ListenForClients()
        {
            this.tcpListener.Start();
        }

        public void SendTCP(TcpClient tcpClient)
        {
            NetworkStream clientStream = tcpClient.GetStream();
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer = encoder.GetBytes(this.msg);
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
    }
}

Клиент:

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace NetClientSideWpf
{
    class Client : Base
    {

        private string messageString;
        public string MessageString
        {
            get { return messageString; }
            set
            {
                messageString = value;
                OnPropertyChanged("MessageString");
            }
        }


        public Client()
        {
            ConnectToServer();
        }

        public void ConnectToServer()
        {
            TcpClient client = new TcpClient();

            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000);

            client.Connect(serverEndPoint);


            NetworkStream clientStream = client.GetStream();

            Thread ServerThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            ServerThread.Start(client);


        }

        private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            byte[] message = new byte[4096];
            int bytesRead;

            while (true)
            {
                bytesRead = 0;

                try
                {
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    break;
                }

                if (bytesRead == 0)
                {
                    break;
                }

                ASCIIEncoding encoder = new ASCIIEncoding();
                MessageString = encoder.GetString(message, 0, bytesRead);
            }
        }
    }
}

любая помощь сделает! :)


person Sagiv b.g    schedule 15.11.2014    source источник


Ответы (1)


Я думаю, что сначала вы должны запустить свой Listener в начале вашего приложения. Затем просмотрите интервалы (таймер) для входящих соединений (TCPListener.Pending http://msdn.microsoft.com/de-de/library/system.net.sockets.tcplistener.pending(v=vs.110.aspx) Если вы получаете соединение, вы должны сохранить TCPClient в списке. При нажатии кнопки отправляйте сообщения своим клиентам.

Я думаю, что это не лучшее решение, но оно без нескольких потоков ;-)

Я написал пример кода, надеюсь, это поможет вам (не тестировалось):

public partial class MainWindow : Window
{

    private TcpListener _server;
    private List<TcpClient> _connectedClients;

    public MainWindow()
    {
        InitializeComponent();

        _server = new TcpListener(IPAddress.Any, 3000);
        _connectedClients = new List<TcpClient>();
        _server.Start();

        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(1); //every 1 sec!
        timer.Tick += timer_Tick;
        timer.Start();
    }

    private void SendMessage(string message)
    {
        ASCIIEncoding encoder = new ASCIIEncoding();
        byte[] buffer = encoder.GetBytes(message);
        foreach (var client in _connectedClients)
        {
            NetworkStream clientStream = client.GetStream();
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
    }


    void timer_Tick(object sender, EventArgs e)
    {
        Debug.WriteLine("Tick");
        Title = "hello";

        if (_server.Pending())
        {
            _connectedClients.Add(_server.AcceptTcpClient());
        }
    }

}
person v_b    schedule 15.11.2014
comment
Я отредактировал свой вопрос, я забыл упомянуть, что все клиенты должны получать одно и то же текстовое сообщение (поэтому я думаю, что нет необходимости их перечислять). Я не возражаю против использования потоков, но как я могу быть уверен, что их достаточно и т. Д. - person Sagiv b.g; 15.11.2014
comment
вы ДОЛЖНЫ перечислить их. У вас есть TcpClient для каждого подключенного клиента. Таким образом, вы должны помнить все, что потом в списке. Я не знаю свойства или метода в TCPListener для отправки сообщения всем клиентам. - person v_b; 15.11.2014
comment
Хорошо, я постараюсь написать образец! - person v_b; 15.11.2014
comment
Кажется, работает просто отлично, так что спасибо :) одна вещь меня беспокоит ... проверка соединения каждую 1 секунду, не потребляет ли это ресурсов? поэтому ты сказал, что считаешь это не лучшим решением? - person Sagiv b.g; 15.11.2014
comment
правильный! Я бы использовал поток с циклом while и проверял там входящие соединения. но вы должны заблокировать свой список клиентов. - person v_b; 18.11.2014
comment
куда бы вы поместили цикл while, чтобы получить тот же результат, но используя меньше ресурсов? - person Sagiv b.g; 18.11.2014
comment
Можете ли вы показать мне, какой код или что вы сделали в файле класса DispatcherTimer, я пытаюсь увидеть, как вы настроили таймер, это файл класса - person shawn; 29.11.2014
comment
Кроме того, у меня возникла проблема, мой сервер больше не доступен для других клиентов, когда один из них отключается, когда у меня три клиента вошли в чат на многоклиентском сервере, он отлично работает, но когда один отключается, два других больше не могут переключать пакеты . Можете ли вы помочь предоставить некоторый код, чтобы сервер оставался подключенным для других клиентов, когда один из них отключается? - person shawn; 29.11.2014