2014-08-11 2 views
2

Во-первых, я не знаю, является ли Stackoverflow лучшим сайтом для публикации такого сообщения, но я не знаю других сайтов, подобных этому.Simple One Thread Socket - TCP-сервер

Чтобы понять правильную программирование tcp в C#, я решил сделать все возможное с нуля. Вот что я хочу знать (не в правильном порядке: - Simple One Thread Socket Server (эта статья) - Простой многопоточный сервер Socket (я не знаю, как, потому что потоки сложны) - Простая резьба сервер (поставить управление клиентом в другом потоке) - несколько потоков сокеты сервера - Использование TcpListener - Использование асинхронного/ОЖИДАНИИ - Использование задач конечная цель состоит в том, чтобы знать, как сделать лучший сервер TCP, не просто копировать/вставить некоторые части прибывают, но все правильно понимают.

Итак, это моя первая часть: один поток tcp-сервера.

Существует мой код, но я не думаю, что кто-то будет что-то исправить, потому что это довольно копия MSDN: http://msdn.microsoft.com/en-us/library/6y0e13d3(v=vs.110).aspx

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 

namespace SimpleOneThreadSocket 
{ 
    public class ServerSocket 
    { 
     private int _iPport = -1; 
     private static int BUFFER_SIZE = 1024; 
     private Socket _listener = null; 

     public ServerSocket(int iPort) 
     {    
      // Create a TCP/IP socket. 
      this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

      // Save the port 
      this._iPport = iPort; 
     } 

     public void Start() 
     { 
      byte[] buffer = null; 
      String sDatasReceived = null; 

      // Bind the socket to loopback address 
      try 
      { 
       this._listener.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, _iPport)); 
       this._listener.Listen(2); 
      } 
      catch (Exception e) 
      { 
       System.Console.WriteLine(e.ToString()); 
      } 

      // Listening 
      try 
      { 
       Console.WriteLine("Server listening on 127.0.0.1:" + _iPport); 

       while (true) 
       { 
        Socket client = this._listener.Accept(); 
        Console.WriteLine("Incoming connection from : " + IPAddress.Parse(((IPEndPoint)client.RemoteEndPoint).Address.ToString()) + ":" + ((IPEndPoint)client.RemoteEndPoint).Port.ToString()); 

        // An incoming connection needs to be processed. 
        while (true) 
        { 
         buffer = new byte[BUFFER_SIZE]; 
         int bytesRec = client.Receive(buffer); 
         sDatasReceived += Encoding.ASCII.GetString(buffer, 0, bytesRec); 
         if (sDatasReceived.IndexOf("<EOF>") > -1) 
         { 
          // Show the data on the console. 
          Console.WriteLine("Text received : {0}", sDatasReceived); 

          // Echo the data back to the client. 
          byte[] msg = Encoding.ASCII.GetBytes(sDatasReceived); 

          client.Send(msg); 

          sDatasReceived = ""; 
          buffer = null; 
         } 
         else if (sDatasReceived.IndexOf("exit") > -1) 
         { 
          client.Shutdown(SocketShutdown.Both); 
          client.Close(); 

          sDatasReceived = ""; 
          buffer = null; 

          break; 
         } 
        } 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.ToString()); 
      } 
     } 
    } 
} 

Но у меня есть несколько вопросов о том, что:

  • Listen Метод из Socket имеет параметр: backlog. Согласно MSDN, отставание - это количество доступных соединений. Я не знаю, почему, когда я ставлю 0, я могу подключиться к моему серверу с несколькими сеансами Telnet. EDIT: 0 & 1 оба разрешают 2 соединения (1 текущий, 1 незавершенный), 2 позволяют 3 соединения (1 текущий, 2 ожидающих ожидания) и т. Д. Поэтому я не очень хорошо понял смысл MSDN.

  • Можете ли вы подтвердить, что метод Accept будет принимать каждое соединение один за другим, поэтому я вижу текст из разных сеансов Telnet на моем сервере?

  • Можете ли вы подтвердить (мой сервер является библиотекой C#) Я не могу убить мой сервер (с помощью такого кода), не убивая процесс? Это возможно с помощью потоков, но это произойдет позже.

Если что-то не так в моем коде, пожалуйста, помогите мне :)

Я скоро вернусь с простым множественным сервером сокета поток, но я не знаю, как (я думаю, что один шаг доступный перед использованием потоков или async/await).

ответ

7

Прежде всего, сделайте все возможное, чтобы не узнать об этом. Если вы можете использовать сервер SignalR, сделайте это. Существует нет такой вещи как «простой» сервер сокетов на уровне TCP/IP.

Если вы настаиваете на болезненном маршруте (то есть, изучая надлежащий дизайн сервера TCP/IP), то есть чему поучиться. Во-первых, примеры MSDN являются, как известно, плохими отправными точками; они едва работают и склонны не обрабатывать какие-либо условия ошибки, что абсолютно необходимо в реальном мире при работе на уровне TCP/IP. Подумайте о них в качестве примеров того, как вызвать методы, не примеры сокетов клиентов или серверов.

У меня есть TCP/IP FAQ, которые могут вам помочь, включая описание backlog parameter.Это количество соединений, которые ОС будет принимать от вашего имени, прежде чем ваш код приблизится к их принятию, и это всего лишь намек.

Чтобы ответить на другие ваши вопросы: один вызов Accept будет принимать одно новое соединение сокета. В написанном коде есть бесконечный цикл, поэтому он будет работать как любой другой бесконечный цикл; он будет продолжать выполнение до тех пор, пока не встретит исключение или его поток не будет прерван (что происходит при завершении процесса).

Если что-то не так в моем коде, пожалуйста, помогите мне

О, да. С этим кодом много чего не так. В конце концов, это пример сокета MSDN. :) Сверху моей головы:

  1. Размер буфера - произвольное значение, довольно низкое. Я бы начал с 8K сам, так что можно получить полный пакет Ethernet в одном чтении.
  2. Bind явно использует обратный адрес. ОК, чтобы поиграть, я думаю, но не забудьте установить это в IPAddress.Any в реальном мире.
  3. backlog параметр OK для тестирования, но должен быть int.MaxValue на истинном сервере, чтобы включить динамическое отставание в современных серверных ОС.
  4. Код будет пролегать первым catch и попытаться установить Accept после ошибки Bind/Listen.
  5. Если возникло какое-либо исключение (например, от Listen или Receive), то весь сервер отключается. Обратите внимание, что завершение клиентского сокета приведет к исключению, которое должно быть зарегистрировано/проигнорировано в должно быть, но это остановит этот сервер.
  6. Буфер чтения перераспределяется каждый раз через цикл, хотя старый буфер больше никогда не используется.
  7. ASCII - кодировка с потерями.
  8. Если клиент полностью отключается без отправки <EOF>, то сервер входит в бесконечный цикл занятости.
  9. Полученные данные не разделены должным образом на сообщения; возможно, что эхо-сообщение содержит все одно сообщение и часть другого. В этом конкретном примере это не имеет значения (поскольку это всего лишь эхо-сервер, и он использует ASCII вместо реальной кодировки), но этот пример скрывает тот факт, что вам необходимо правильно обрабатывать сообщения в любом реальном приложении.
  10. Декодирование должно быть выполнено после сообщение кадрирование. Это не обязательно для ASCII (кодирование с потерями), но это необходимо для любых реальных кодировок, таких как UTF8.
  11. Поскольку сервер принимает или отправляет только в любое время (и никогда не оба), он не может обнаружить или восстановить из ситуации с разомкнутым гнездом. Полуоткрытый сокет приведет к зависанию этого сервера.
  12. Сервер поддерживает только одно соединение за раз.

Это было сделано после краткого прочтения. Там может быть и больше.

+0

Любовь необъяснимых downvotes. Кто-нибудь хочет высказаться? Вы знаете, на самом деле указывают на один неверный момент в моем ответе? ;) –

+1

Бог, это код на MSDN? Это должно отвечать за часть многих вопросов TCP, на которые я отвечаю. – usr

+0

@usr: Я серьезно планирую сделать серию блога, разрывая примеры сокетов MSDN, по одному сообщению на образец. Шаг за шагом, объясняя, почему это неправильно, и как это сделать правильно. Кстати, исходный пример MSDN еще хуже (привязывается к 'Dns.Resolve (Dns.GetHostName())) ... Но я планировал эту серию блога годами; не стесняйтесь принимать эту идею и бегать с ней! :) –