2016-05-02 5 views
1

TL; DR: У меня есть сервер C#, который управляет одним клиентом. Каков наилучший способ заставить его обрабатывать несколько клиентов?C# сокеты с несколькими клиентами (подход с несколькими потоками)

Полное объяснение:
Я реализует C# серверное приложение сокета, который нужно обрабатывать несколько клиентов одновременно.

Моя логика такова:

  1. Множественные клиенты подключаются к серверу, передачи и приема данных (в данном случае простой текст - думать об этом как посланник).
  2. У сервера есть отдельный поток для каждого клиента, поток, который отправляет ответы.
  3. Каждый из этих потоков для каждого клиента порождает дополнительный поток, который прослушивает данные соответствующего клиента (я не знаю, хорошо ли этот подход, я могу рассмотреть некоторые асинхронные чтения, но я не понимаю, как они работают как я они никогда не использовали их).
  4. Сервер различает каждый клиент, используя список, мы можем предположить, что список содержит IP-адрес каждого клиента и уникальный идентификатор для легкого распознавания (предположим, имя пользователя).

код у меня до сих пор управляет только один клиент:

public partial class Form1 : Form 
{ 
    TcpListener listener; 
    Socket clientSock; 
    Thread listenThread, receiveThread; 
    string sendMsg, recvMsg; 
    byte[] sendMsgRaw, recvMsgRaw; 

    public Form1() { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) { 
     // Handler for onExit. 
     AppDomain.CurrentDomain.ProcessExit += new EventHandler(onExit); 
    } 

    private void listenButton_Click(object sender, EventArgs e) { 
     // Launch thread that listens for clients attempting to connect. 
     listener = new TcpListener(8888); // I know this method is deprecated, will fix. 
     listener.Start(); 

     listenThread = new Thread(new ThreadStart(listenForClients)); 
     listenThread.Start(); 
    } 

    private void sendButton_Click(object sender, EventArgs e) { 
     // Send message to client. 
     sendMsg = sendTextbox.Text; 
     sendMsgRaw = Encoding.ASCII.GetBytes(sendMsg + "\r\n"); 
     clientSock.Send(sendMsgRaw); 
    } 

    private void listenForClients() { 
     // Listen for connection attempts. 
     clientSock = listener.AcceptSocket(); 
     disableButton(listenButton); 
     receiveThread = new Thread(new ThreadStart(readFromClient)); 
     receiveThread.Start(); 
    } 

    private void readFromClient() { 
     // Read response from client. 
     while(true) { 
      recvMsg = ""; 
      recvMsgRaw = new byte[1024]; 

      clientSock.Receive(recvMsgRaw); 
      recvMsg = Encoding.ASCII.GetString(recvMsgRaw); 
      appendRecvTextbox(recvMsg); 
     } 
    } 

    private void appendRecvTextbox(string message) { 
     // Append client's reply to a textbox. 
     if(InvokeRequired) { 
      this.Invoke(new Action<string>(appendRecvTextbox), new object[] { message }); 
      return; 
     } 
     recvTextbox.AppendText(message + Environment.NewLine); 
    } 

    private void disableButton(Button btn) { 
     // Disable a button. 
     if(InvokeRequired) { 
      this.Invoke(new Action<Button>(disableButton), new object[] { btn }); 
      return; 
     } 
     btn.Enabled = false; 
    } 

    private void onExit(object sender, EventArgs e) { 
     // Executed on program exit. 
     clientSock.Close(); 
    } 
} 

Вот скриншот формы я имею в виду: http://prntscr.com/azkaed

В основном, когда элемент ListBox (клиент) является выбранная, область полученных данных изменится в соответствии с тем, что было получено этим клиентом.
И когда выбран тот же элемент списка (клиент) и отправляется сообщение, он будет отправлен этому клиенту и только этому клиенту (без многоадресной рассылки или чего-то подобного).

FYI позже, я реализую передачу файлов, следуя тем же принципам.

Я не прошу вас, смиренные люди, запросить у меня мое заявление, это непродуктивно, и я ничего не узнаю. Я хочу понять и обработать, как все это работает. Очевидно, что образцы кода, которые помогут мне понять, очень приветствуются. Потенциальные проблемы и вопросы:

  1. Является ли моя логика ошибочной? Если да, то что вы предлагаете? Мне кажется, что я использую слишком много потоков, и я не буду разбираться в потоке, который отправляется клиенту, и потоке, который читается от клиента.
  2. Когда я создаю поток, что он наследует? Будет ли он наследовать КОПИЮ сокета (например, слушателя) или самого исходного сокета?
  3. Если я создаю столько потоков, как я могу различать основной поток для клиента (который отправляет данные) и связанный с ним поток чтения?
  4. Должен ли я пойти на UDP? Протокол без установления соединения имеет смысл в моей голове, я могу просто отправлять и получать на/из IP без активного подключения, которое мне нужно позаботиться. Но я беспокоюсь о ненадежности UDP. Тем более, что после этого я намерен реализовать передачи файлов.
  5. После того как я закодировал этот сервер, я заметил, что я не привязывал сокет к порту, но он счастливо общается с клиентом cpp.Я помню из своих сетевых классов еще в колледже, которые нам нужно связать, если мы используем протокол TCP. Я делаю вывод, что привязка не нужна. Почему это?

Итак, главная проблема: в текущем состоянии приложение обрабатывает один клиент. Как я могу заставить его обрабатывать несколько клиентов? Обратите внимание, что я выбрал многопоточность, потому что у меня будет ограниченное количество клиентов, подключенных одновременно - до 10 клиентов.

Я знаю, что это непростой вопрос, который я задаю, но каждый ответ будет глубоко оценен.
Если это имеет значение, клиенты будут записаны в амальгаме таких языков, как c/cpp, java, python, я даже рассматриваю PHP. Это для единственной цели обучения. Но я сомневаюсь, что это имеет значение. Клиентам гораздо проще программировать, так как существует меньше логики, поэтому я не требую никакой помощи там.

Благодарим вас за любые предложения.

ответ

2

Во-первых, есть по этой ссылке: http://www.codeproject.com/Articles/429144/Simple-Instant-Messenger-with-SSL-Encryption-in-Cs

Теперь я anwser ваши вопросы:

  1. Там нет такого понятия, как "слишком много нитей". Вы можете сделать свой сервер, у которого будет 1 поток за запрос. Этот поток будет читать и записывать данные для одного запроса (так работают все веб-службы, обычно они обрабатывают до 100 потоков на пользователя). Эффективность. Есть вещь, называемая обмен нитями, но не беспокойтесь об этом.

  2. Вы можете сделать один поток для пользователя, как в ссылке выше. Или вы можете пойти статично, как webservices и сделать 1 поток за запрос.

  3. Вы должны смотреть на потоки не как абстрактную вещь, а скорее как объект. Если бы вы всегда сохраняли только один поток в одном объекте, у вас никогда не будет проблемы с «знанием», ведьма - это где. Ofc вы должны помнить о замках и прочее, но в пределах одного объекта один поток.

  4. UDP не гарантирует, что ваши пакеты приземлятся там, где они были отправлены. TCP, по крайней мере, попытается сделать это. Таким образом, чтобы упростить работу с TCP.

  5. У меня есть только ключ, что вы можете использовать по умолчанию один случайно.

Надеюсь, это поможет.

Смежные вопросы