Что принимает на вход конструктор класса socket

Обновлено: 03.05.2024

Термин «сетевое программирование» относится к написанию программ, которые выполняются на нескольких устройствах (компьютерах), в которых все устройства подключены друг к другу с помощью сети.

Содержание

Протоколы TCP и UDP

  • TCP - TCP - это протокол управления передачей, который обеспечивает надежную связь между двумя приложениями. В Java TCP обычно используется через Интернет-протокол, который называется TCP/IP.
  • UDP - UDP - это протокол пользовательских дейтаграмм, протокол без установления соединения, который позволяет передавать пакеты данных между приложениями.

Данная глава надлежащим образом раскрывает следующие две темы:

  • Программирование сокетов - это наиболее широко используемая концепция в сетевой конфигурации, о которой было подробно изложено.
  • Обработка URL (унифицированный указатель ресурсов) - этот процесс будет рассмотрен отдельно. Перейдите по ссылке, чтобы узнать об обработке URL на языке Java.

Программирование сокетов

В Java сокеты обеспечивают механизм связи между двумя компьютерами, использующими TCP. Клиентская программа создает сокет на своем конце связи и пытается подключить этот сокет к серверу.

Когда соединение установлено, сервер создает объект сокета на своем конце связи. Клиент и сервер теперь могут общаться, записывая и считывая данные с сокета.

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

  • Сервер создает экземпляр объекта ServerSocket, определяющий, по какому номеру порта должна происходить связь.
  • Сервер вызывает метод accept() класса ServerSocket. Этот метод ожидает, пока клиент не подключится к серверу по указанному порту.
  • По завершению ожидания сервера клиент создает экземпляр объекта сокета, указывая имя сервера и номер порта подключения.
  • Конструктор класса Socket осуществляет попытку подключить клиента к указанному серверу и номеру порта. Если связь установлена, у клиента теперь есть объект Socket, способный связываться с сервером.
  • На стороне сервера метод accept() возвращает ссылку к новому сокету на сервере, который подключен к клиентскому сокету.

После того, как соединения установлены, связь может происходить с использованием потоков входных/выходных данных. Каждый сокет имеет и OutputStream (поток выходных данных), и InputStream (поток входных данных). OutputStream клиента подключен к InputStream сервера, а InputStream клиента подключен к OutputStream сервера.

TCP является двусторонним протоколом связи, поэтому данные могут передаваться по обоим потокам одновременно. Ниже приведены полезные классы, предоставляющие полный набор методов внедрения сокетов.

Конструкторы класса ServerSocket

Класс ServerSocket имеет четыре конструктора:

Конструктор и описание
1public ServerSocket(int port) throws IOException
Попытки создания серверного сокета, связанного с указанным портом. Исключение происходит, если порт уже связан другим приложением.
2public ServerSocket(int port, int backlog) throws IOException
Как и в предыдущем конструкторе, параметр backlog указывает, сколько входящих клиентов нужно сохранить в очереди ожидания.
3public ServerSocket(int port, int backlog, InetAddress address) throws IOException
Как и в предыдущем конструкторе, параметр InetAddress указывает локальный IP-адрес для осуществления привязки. InetAddress используется в серверах, которые могут иметь несколько IP-адресов, что позволяет серверу указывать IP-адрес приема запросов клиентов.
4public ServerSocket() throws IOException
Создает непривязанный сокет сервера. При использовании этого конструктора используйте метод привязки (), когда будете готовы привязать сокет сервера.

Если конструктор ServerSocket не выдает исключение, это означает, что ваше приложение успешно связано с указанным портом и готово к клиентским запросам.

Методы класса ServerSocket

Ниже приведены некоторые из распространенных методов в Java класса ServerSocket.

Методы и описание
1public int getLocalPort()
Возвращает порт, который прослушивает сокет сервера. Этот метод полезен, если вы передали 0 в качестве номера порта в конструкторе и позволили серверу найти порт.
2public Socket accept() throws IOException
Ожидает входящего клиента. Этот метод блокируется до тех пор, пока клиент не подключится к серверу на указанном порту или не истечет время ожидания сокета, при условии, что значение времени ожидания было установлено с помощью метода setSoTimeout(). В противном случае этот метод блокируется на неопределенный срок.
3public void setSoTimeout(int timeout)
Устанавливает значение времени ожидания клиента сокетом сервера во время accept().
4public void bind (хост SocketAddress, int backlog)
Привязывает сокет к указанному серверу и порту в объекте SocketAddress. Используйте этот метод, если вы создали ServerSocket с помощью конструктора без аргументов.

Когда ServerSocket вызывает accept(), метод не возвращается, пока клиент не подключится. После того, как клиент все-таки подключится, ServerSocket создает новый сокет для неуказанного порта и возвращает ссылку на этот новый сокет. Теперь между клиентом и сервером существует TCP-соединение, и связь может установиться.

Конструкторы класса Socket

Класс Socket имеет пять конструкторов, которые клиент использует для подключения к серверу.

Конструктор и описание
1public Socket(String host, int port) throws UnknownHostException, IOException.
Этот метод предпринимает попытку подключения к указанному серверу через указанный порт. Если этот конструктор не выдает исключение, то соединение установлено успешно, и клиент подключен к серверу.
2public Socket(InetAddress host, int port) throws IOException
Этот метод идентичен предыдущему конструктору, за исключением того, что хост обозначается объектом InetAddress.
3public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException.
Подключается к указанному хосту и порту, создавая сокет на локальном хосте по указанному адресу и порту.
4public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException.
Этот метод идентичен предыдущему конструктору, за исключением того, что хост обозначается объектом InetAddress вместо строки адреса.
5public Socket()
Создает неподключенный сокет. Используйте метод connect() для подключения такого сокета к серверу.

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

Некоторые методы, изучающие класс сокета, перечислены здесь. Обратите внимание, что и клиент, и сервер имеют объект сокета, поэтому эти методы могут вызываться как клиентом, так и сервером.

Методы класса Socket

Методы класса InetAddress

Этот класс представляет адрес Интернет-протокола (IP). Следующие полезные методы, которые понадобятся при программировании сокетов, представлены ниже:

и описание
1static InetAddress getByAddress(byte[] addr)
Возвращает объект InetAddress с учетом необработанного IP-адреса.
2static InetAddress getByAddress(String host, byte[] addr)
Создает InetAddress на основе предоставленного имени хоста и IP-адреса.
3static InetAddress getByName(String host)
Определяет IP-адрес хоста, учитывая имя хоста.
4String getHostAddress()
Возвращает строку IP-адреса в текстовой форме.
5String getHostName()
Получает имя хоста для данного IP-адреса.
6static InetAddress InetAddress getLocalHost()
Возвращает локальный хост.
7String toString()
Конвертирует этот IP-адрес в адресную строку.

Пример Socket Client

Следующая GreetingClient – это клиентская программа, которая подключается к серверу с помощью сокета, отправляет приветствие, а затем ожидает ответа.

Пример Socket Server

Следующая программа GreetingServer является примером серверного приложения, которое использует класс сокета для прослушивания клиентов по номеру порта, указанному в аргументе командной строки.

Скомпилируйте клиент и сервер, а затем запустите сервер следующим образом:

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Инициализирует новый экземпляр класса Socket.

Перегрузки

Инициализирует новый экземпляр Socket класса для указанного дескриптора сокета.

Инициализирует новый экземпляр класса Socket с помощью указанного значения, возвращенного из объекта DuplicateAndClose(Int32).

Инициализирует новый экземпляр класса Socket, используя указанный тип сокетов и протокол. Если операционная система поддерживает IPv6, этот конструктор создает сокет с двумя режимами; в противном случае он создает сокет IPv4.

Инициализирует новый экземпляр класса Socket, используя заданные семейство адресов, тип сокета и протокол.

Socket(SafeSocketHandle)

Инициализирует новый экземпляр Socket класса для указанного дескриптора сокета.

Параметры

Дескриптор сокета для сокета, который будет инкапсулирован с помощью объекта Socket.

Исключения

handle имеет значение null .

handle не является сокетом или не удалось получить доступ к сведениям о сокете.

Комментарии

Этот метод заполняет Socket экземпляр данными, собранными из предоставленного SafeSocketHandle. Различные операционные системы обеспечивают различные уровни поддержки запроса дескриптора сокета или дескриптора файла для его свойств и конфигурации. Некоторые общедоступные API в результирующем Socket экземпляре могут отличаться в зависимости от операционной системы, например ProtocolType и Blocking.

Применяется к

Socket(SocketInformation)

Инициализирует новый экземпляр класса Socket с помощью указанного значения, возвращенного из объекта DuplicateAndClose(Int32).

Параметры

Информация сокета, возвращенная объектом DuplicateAndClose(Int32).

Комментарии

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

Применяется к

Socket(SocketType, ProtocolType)

Инициализирует новый экземпляр класса Socket, используя указанный тип сокетов и протокол. Если операционная система поддерживает IPv6, этот конструктор создает сокет с двумя режимами; в противном случае он создает сокет IPv4.

Параметры

Одно из значений перечисления SocketType.

Одно из значений перечисления ProtocolType.

Исключения

Сочетание параметров socketType и protocolType приводит к недопустимому сокету.

Комментарии

Параметр socketType указывает тип Socket класса, а protocolType параметр указывает протокол, используемый Socket. Два параметра не являются независимыми. Socket Часто тип неявен в протоколе. Если сочетание Socket типа и типа протокола приводит к недопустимому Socketзначению, этот конструктор создает исключение SocketException.

Если этот конструктор создает исключение SocketException, используйте SocketException.ErrorCode свойство для получения определенного кода ошибки. Получив этот код, ознакомьтесь с документацией по коду ошибки API Windows sockets версии 2, чтобы получить подробное описание ошибки.

Применяется к

Socket(AddressFamily, SocketType, ProtocolType)

Инициализирует новый экземпляр класса Socket, используя заданные семейство адресов, тип сокета и протокол.

Параметры

Одно из значений перечисления AddressFamily.

Одно из значений перечисления SocketType.

Одно из значений перечисления ProtocolType.

Исключения

Сочетание параметров addressFamily , socketType и protocolType приводит к неработоспособному сокету.

Примеры

В следующем примере кода показано, как создать экземпляр Socket класса.

Комментарии

Параметр addressFamily указывает схему адресации, используемую Socket классом, socketType параметр указывает тип Socket класса, а protocolType параметр указывает протокол, используемый Socket. Три параметра не являются независимыми. Некоторые семейства адресов ограничивают, какие протоколы можно использовать вместе с ними, и часто Socket тип неявен в протоколе. Если сочетание семейства адресов, Socket типа и типа протокола приводит к недопустимому Socketтипу, этот конструктор создает исключение SocketException.

Если этот конструктор создает исключение SocketException, используйте SocketException.ErrorCode свойство для получения определенного кода ошибки. Получив этот код, ознакомьтесь с документацией по коду ошибки API Windows sockets версии 2, чтобы получить подробное описание ошибки.

— Тема сегодняшней лекции – Сокеты. Socket в переводе с английского – Розетка.

Ты уже знаешь, что у каждого компьютера в сети есть его уникальный IP-адрес.

— И вот представь, что у тебя есть несколько компьютеров и на каждом компьютере запущено с десяток программ, которые работают с интернетом: Skype, ICQ, и т.д.

И эти программы хотят общаться между собой.

Надо сделать так, чтобы они друг другу «не мешали». Чтобы Skype связывался со Skype’ом, ICQ с ICQ и т.д.

Помнишь, как это проблему решили с URL и веб-серверами?

— Ага, добавили порты.

Это все равно, что в доме сделать много маленьких комнат и объявить его многоквартирным. Каждый порт – это как отдельная квартира.

Так вот IP-адрес – это уникальный номер компьютера, а IP-адрес+порт – это уникальный номер некой «квартиры» (ячейки) в компьютере, которую может занять программа.

Вот такая уникальная ячейка и называется сокет.

У сокета есть его уникальный номер, и с остоит он из IP-адреса и номера порта.

— Не знаю, как ты это понял, но именно так и есть.

— Это моя робоинтуиция подсказала.

— Отлично. Тогда давай немного подробностей.

Сокеты – это фактически самый базовый и самый примитивный способ межсетевого взаимодействия программ.

В Java для работы с сокетами есть два класса. Это классы Socket и ServerSocket.

ServerSocket – это специальный класс, объекты которого выполняют роль сервера – т.е. могу обслуживать запросы, пришедшие на определенный сокет.

— Все очень напоминает скачивание файла из интернета.

— Это потому, детка, что там тоже используются сокеты.

Сокеты используются в основе всего связанного с сетью, ну или почти всего.

Вот тут можешь почитать дополнительную информацию.

— Спасибо за лекцию, Риша.

— Это еще не все. Размечтался.

Теперь мы разберемся, как работает серверный сокет.

Он работает чуток посложнее.

Хочу обратить твое внимание на несколько моментов.

Момент 1: Для создания сокета нужно указывать IP-адрес (или домен) и порт. Для создания серверного сокета – только порт. Серверный сокет появляется только на том компьютере, где его создали.

Момент 2: У класса ServerSocket есть метод accept(), который, если его вызвать, будет ждать входящее соединение. Т.е. метод будет выполняться бесконечно долго, пока какой-то клиентский сокет не попробует обратиться к нему. Тогда метод accept() примет соединение, создаст объект сокет для коммуникации и после этого вернет этот объект.

Когда ты создаешь серверный сокет, фактически появляется порт, к которому могут подключаться сокеты с других компьютеров. Но для этого им надо правильно указать номер порта нашего сокета и IP-адрес нашего компьютера. Ну, или его доменное имя.

Вот тебе интересный пример, можешь попробовать разобрать его и выполнить:

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

В далеком для меня 2010 году я писал статью для начинающих про сокеты в Python. Сейчас этот блог канул в небытие, но статья мне показалась довольно полезной. Статью нашел на флешке в либровском документе, так что это не кросспост, не копипаст — в интернете ее нигде нет.


Что это

Для начала нужно разобраться что такое вообще сокеты и зачем они нам нужны. Как говорит вики, сокет — это программный интерфейс для обеспечения информационного обмена между процессами. Но гораздо важнее не зазубрить определение, а понять суть. Поэтому я тут постараюсь рассказать все как можно подробнее и проще.

Существуют клиентские и серверные сокеты. Вполне легко догадаться что к чему. Серверный сокет прослушивает определенный порт, а клиентский подключается к серверу. После того, как было установлено соединение начинается обмен данными.


Рассмотрим это на простом примере. Представим себе большой зал с множеством небольших окошек, за которыми стоят девушки. Есть и пустые окна, за которыми никого нет. Те самые окна — это порты. Там, где стоит девушка — это открытый порт, за которым стоит какое-то приложение, которое его прослушивает. То есть, если, вы подойдете к окошку с номером 9090, то вас поприветствуют и спросят, чем могут помочь. Так же и с сокетами. Создается приложение, которое прослушивает свой порт. Когда клиент устанавливает соединение с сервером на этом порту именно данное приложение будет ответственно за работу этим клиентом. Вы же не подойдете к одному окошку, а кричать вам будут из соседнего :)

После успешной установки соединения сервер и клиент начинают обмениваться информацией. Например, сервер посылает приветствие и предложение ввести какую-либо команду. Клиент в свою очередь вводит команду, сервер ее анализирует, выполняет необходимые операции и отдает клиенту результат.

Сервер

Сейчас создайте два файла — один для сервера, а другой для клиента.

В Python для работы с сокетами используется модуль socket:

Прежде всего нам необходимо создать сокет:

Здесь ничего особенного нет и данная часть является общей и для клиентских и для серверных сокетов. Дальше мы будем писать код для сервера. Это вполне логично — зачем нам писать клиентское приложение, если некуда подключаться :)

Теперь нам нужно определиться с хостом и портом для нашего сервера. Насчет хоста — мы оставим строку пустой, чтобы наш сервер был доступен для всех интерфейсов. А порт возьмем любой от нуля до 65535. Следует отметить, что в большинстве операционных систем прослушивание портов с номерами 0 — 1023 требует особых привилегий. Я выбрал порт 9090. Теперь свяжем наш сокет с данными хостом и портом с помощью метода bind, которому передается кортеж, первый элемент (или нулевой, если считать от нуля) которого — хост, а второй — порт:

Теперь у нас все готово, чтобы принимать соединения. С помощью метода listen мы запустим для данного сокета режим прослушивания. Метод принимает один аргумент — максимальное количество подключений в очереди. Напряжем нашу бурную фантазию и вспомним про зал с окошками. Так вот этот параметр определяет размер очереди. Если он установлен в единицу, а кто-то, явно лишний, пытается еще подстроится сзади, то его пошлют :) Установим его в единицу:

Ну вот, наконец-то, мы можем принять подключение с помощью метода accept, который возвращает кортеж с двумя элементами: новый сокет и адрес клиента. Именно этот сокет и будет использоваться для приема и посылке клиенту данных.

Вот и все. Теперь мы установили с клиентом связь и можем с ним «общаться». Т.к. мы не можем точно знать, что и в каких объемах клиент нам пошлет, то мы будем получать данные от него небольшими порциями. Чтобы получить данные нужно воспользоваться методом recv, который в качестве аргумента принимает количество байт для чтения. Мы будем читать порциями по 1024 байт (или 1 кб):

Как мы и говорили для общения с клиентом мы используем сокет, который получили в результате выполнения метода accept. Мы в бесконечном цикле принимаем 1024 байт данных с помощью метода recv. Если данных больше нет, то этот метод ничего не возвращает. Таким образом мы можем получать от клиента любое количество данных.

Дальше в нашем примере для наглядности мы что-то сделаем с полученными данными и отправим их обратно клиенту. Например, с помощью метода upper у строк вернем клиенту строку в верхнем регистре.

Теперь можно и закрыть соединение:

Собственно сервер готов. Он принимает соединение, принимает от клиента данные, возвращает их в виде строки в верхнем регистре и закрывает соединение. Все просто :) В итоге у вас должно было получиться следующее:

Клиент

Думаю, что теперь будет легче. Да и само клиентское приложение проще — нам нужно создать сокет, подключиться к серверу послать ему данные, принять данные и закрыть соединение. Все это делается так:

Думаю, что все понятно, т.к. все уже разбиралось ранее. Единственное новое здесь — это метод connect, с помощью которого мы подключаемся к серверу. Дальше мы читаем 1024 байт данных и закрываем сокет.

Данная статья предназначена для таких же новичков как и я. Вся информация в этой статье основывается на моем опыте создания одного единственного веб-сервера, который был создан в рамкам учебного проекта на 3 курсе по специальности 09.02.07 СПО.

Веб-сервер

Прежде чем писать свой веб сервер, нам нужно понять что это и как он работает.

Под веб-сервером подразумевают две вещи:

В данной статье мы будем рассматривать веб-сервер, как программное обеспечение.

Веб-сервер работает благодаря такой архитектуре как клиент - сервер

Рисунок 1 - Блок-схема архитектуры клиент-сервер

Рисунок 1 - Блок-схема архитектуры клиент-сервер

Чтобы было понятнее, разобьем работу архитектуры по пунктам:

Формирование запроса клиентом

Отправка запроса на сервер

Получение запроса на сервере

Обработка запроса и формирование ответа

Отправка ответа клиенту

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

TCP/IP (Transmission Control Protocol/Internet Protocol) - два основных протокола на которых строится весь современный интернет. TCP предназначен для передачи данных между участниками сети, а IP является межсетевым протоколом, который используется для обозначения участников сети.

Вот мы и перешли от слов к практике, но перед этим, нам нужно определиться, с помощью чего мы будем писать наш веб-сервер ? Нашему вниманию представляются несколько классов которые могут нам в этом помочь:

TcpListener - прослушивает входящие TCP соединения по паре ip:port. Может от моей криворукости, больше чем в этом уверен, или от чего-то другого, у меня получалось так, что TcpListener не совсем подходит для этой задачи, так как при отправке пакетов клиенту, некоторые файлы просто не приходили и каждый раз количество файлов разнилось.

В данной статье мы рассмотрим только вариант на основе класса Socket , кому интересно знать, как реализовать веб-сервер на TcpListener , то вот ссылка на статью другого автора.

Для начала мы должны создать 2 класса(они должны располагаться в двух новых файлах):

Server - этот класс будет обозначать наш сервер и он будет принимать входящие подключения

Client - этот класс будет обозначать нашего клиента, в этом классе будет проходить вся обработка запроса

Начнем заполнять класс Server . Для начала мы должны добавить в наш класс библиотеки, которые нам понадобятся:

Затем в классе мы должны создать переменные которыми будем оперировать:

Теперь создадим конструктор для нашего класса. Так как Socket работает по ip:port, то и наш конструктор будет принимать первым аргументом ip, а вторым port:

Ip мы будем задавать в виде строки и преобразовать его к классу IPEndPoint через класс IPAddress . Порт самое простое, просто обычное число типа int . Думаю самое непонятное для вас сейчас, это конструктор класса Socket :

AddressFamily – перечисление, которое обозначает то, с какой версией ip адресов мы будем работать. InterNetwork говорит о том что мы используем IPv4.

SocketType – перечисление, обозначает тип сокета и какое подключение будет устанавливаться. В нашем случаем мы будем работать с типом подключения Stream .

ProtocolType – перечисление, обозначает то, какой тип подключений мы будем принимать. Tcp , означает то что мы будем работать с протоколом TCP.

После конструктора нам стоит создать две функции, первая будет инициализировать работу нашего сервера, а вторая останавливать его соответственно:

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

После мы заполняем наше условие, если сервер выключен то:

Функция Bind класса Socket означает, что слушатель будет работать по определенному ip:port, который мы передаем в эту функцию.

Функция Listen начинает прослушивание, как аргумент мы передаем в нее переменную типа int , который означает возможное кол-во клиентов в очереди на подключение к сокету.

Теперь приступим к реализации многопоточности. Делать мы ее будем на основе такого класса как ThreadPool . Нет, конечно можно было сделать проще:

Но такой способ не эффективен, так как он будет просто создавать новые потоки для обработки входящего соединения, тем самым тормозя работу сервера, ведь как никак потоки у нашего процессора не безграничны. Поэтому мы берем и вставляем этот кусок кода в наше условие(после Active = true ):

ThreadPool.QueueUserWorkItem(WaitCallback, object) - добавляет в очередь функции, которые должны выполниться

WaitCallback(ClientThread) - принимает функцию и возвращает ответ о ее выполнении

Listener.AcceptTcpClient() - аргумент, который будет передаваться в функцию

Функция будет циклически прослушивать входящие соединения. Listener.Accept() будет временно останавливать цикл, до тех пор, пока не придет запрос на подключение.

Теперь перейдем к нашей функции остановки сервера:

В ней мы пишем условие, обратное тому которое было в Start , т.е тут мы должны проверять включен ли сервер.

Функцией Close класса Socket мы прекращаем прослушивание. Затем мы меняем значение переменной Active на false .

Думаю по функции Start вы заметили, что там присутствовала такая функция как ThreadClient , пришло время создать и ее. Она будет отвечать за создание нового клиента, который подключается к нашему серверу:

Так как делегат WaitCallback требует, чтобы аргументом являлся простой тип object , то функция соответственно будет тоже принимать тип object , который мы будем не явным образов преобразовывать в класс Socket .

Пришло время и для описания класса Client . Для начала подключим нужные нам библиотеки в файле:

Method - хранит метод, с помощью которого делается запрос

RealPath – хранит полный путь до файла на нашем сервере(пример: C:\Users\Public\Desktop\Server\www\index.html)

File - хранит не полный путь до файла(пример: \www\index.html)

Теперь давайте создадим саму функцию, которая будет парсить заголовки:

Она будет возвращать саму структуру, тогда объявление структуры будет выглядеть так:

Теперь опишем тело функции:

Объяснять принцип работы регулярных выражений я не буду, поэтому в конце статьи есть ссылка на документацию.

При присвоении значения переменной RealPath у объекта структуры result , я написал: AppDomain.CurrentDomain.BaseDirectory - это означает, что мы берем путь до нашего exe файла, пример: C:\Users\Public\Desktop\Server, а затем мы подставляем неполный путь до нашего файла: File , и тогда наш путь будет выглядеть так: C:\Users\Public\Desktop\Server\ + \www\index.html = C:\Users\Public\Desktop\Server\www\index.html . Т.е, файлы сайта будут находиться относительно нашего сервера.

Теперь давайте напишем функцию, которая будет возвращать нам расширения нашего файла, назовем ее FileExtention :

Опять же, делаем это с помощью регулярных выражений.

Эта структура была сделана для удобства, так как когда мы будет парсить большое кол-во заголовков, то лучше если они будут храниться в одном месте.

Создадим в классе Client переменные:

У конструктора нашего класс будет всего 1 аргумент, который будет принимать Socket :

После в конструкторе, мы должны присвоить нашей переменной client наш аргумент и начать принимать данные от клиента:

Код представленный выше описывает то, как сервер принимает запросы от клиента:

data - массив который принимает байты

request - запрос в виде строки

client.Receive(data) - считывает приходящие байты и записывает их в массив.

После того как мы запишем принятые данные от клиента в массив байтов data , мы должны привести это в понятный вид, для этого мы воспользуемся классом Encoding , с помощью которого переведем байты в символы:

Теперь настало время проверок и парсинга наших заголовков.

Первое условие проверяет, пришел ли какой-то запрос вообще ? Если нет, то мы отсоединяем клиента и выходим из функции:

Ну и наконец последняя проверка в этой функции, если файл по указанному пути Headers.RealPath существует, то мы начинаем работать с этим файлом, иначе выводи ошибку:

Перед описанием основной функции GetSheet , которая будет возвращать пользователю ответ, мы создадим пару функций.

Первая функция SendError , она будет возвращать код ошибки пользователю:

html - представляет разметку нашей страницы

headers - представляет заголовки

data - массив байтов

client.Send(data, data.Length, SocketFlags.None); - отправляет данные клиенту

client.Close(); - закрывает нынешнее соединение

Теперь создадим функцию, которая будет возвращать тип контента, так как в данной статье представлена простая версия сервера, то мы ограничимся типами: text и image. Тип контента мы выводим для того, чтобы отправленный нами файл мог опознаться, записываем мы это значение в специальном заголовке Content-Type (пример: Content-Type: text/html ):

Теперь опишем нашу последнюю функцию GetSheet и можно будет тестировать наш сервер:

Теперь опишем тело оператора try :

После того как мы переведем наши заголовки в массив байтов мы отправим их клиенту с помощью метода Send() класса Socket , который принимает следующие параметры:

byte[] - массив байтов

byte[].Length - длинна передаваемого массива

SocketFlags - перечисление, которое представляет поведение сокета при отправке и получении пакетов. Значение None обозначает что флаги не используются

Так как у нас многопоточный сервер, который работает на ThreadPool, то для начала в файле который содержит функцию Main мы подключим библиотеку: System.Threading , а затем укажем минимальное кол-во потоков, которое он может использовать:

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

Теперь зададим максимальные значения для нашего пула:

После чего мы просто инициализируем наш класс Server в функции и запускаем его:

Давайте создадим в папке, где располагается наш exe(пример пути. /project/bin/Debug/netx.x/ - где project имя вашего проекта) файл простой html файл:

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

Ссылка на сервер на GitHub, в данной версии сервера реализована поддержка php.

Читайте также: