Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Разработка клиентского приложения передачи электронной почты с применением протоколов SMPT и UDP на платформе Win32

Содержание:

Введение

Простой протокол передачи почты (SMTP)

Агент передачи почты - основной компонент системы передачи почты Internet. Как уже говорилось, МТА как бы представляет данный сетевой компьютер для сетевой системы электронной почты. Пользователи редко имеют дело с МТА, поскольку он не вполне "дружелюбен", однако без него не обходится ни одна почтовая система. После того как UA пошлет сообщение в выходную очередь, за дело принимается МТА. Он извлекает сообщение и посылает его другому МТА. Этот процесс продолжается до тех пор, пока сообщение не достигнет компьютера-получателя. Для передачи сообщений по TCP-соединению большинство МТА пользуются протоколом SMTP. Сообщения форматированы по правилам виртуального сетевого терминала (NVT), то есть в NVT ASCII. NVT подобен виртуальному сетевому протоколу и нужен затем, чтобы скрыть различия в восприятии разными компьютерами разных символов, например переводов каретки, переводов строки, маркеров конца строки, очистки экрана и т. д. Символ в NVT состоит из семи битов набора ASCII и является буквой, цифрой или знаком пунктуации. Семи битный набор ASCII часто называется NVT ASCII.

Модель протокола.

Взаимодействие в рамках SMTP строится по принципу двусторонней связи, которая устанавливается между отправителем и получателем почтового сообщения. При этом отправитель инициирует соединение и посылает запросы на обслуживание, а получатель - отвечает на эти запросы. Фактически отправитель выступает в роли клиента, а получатель - сервера.

http://www2.icmm.ru/~masich/win/lexion/mail/mailfig5.gif

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

Команды SMTP

Простой протокол передачи почты обеспечивает двухсторонний обмен сообщениями между локальным клиентом и удаленным сервером МТА. МТА-клиент шлет команды МТА-серверу, а он, в свою очередь, отвечает клиенту. Другими словами, протокол SMTP требует получать ответы (они описаны в этой главе) от приемника команд SMTP. Обмен командами и ответами на них называется почтовой транзакцией (mail transaction). Данные, как мы уже говорили, передаются в формате NVT ASCII. Кроме того, команды тоже передаются в формате NVT ASCII. Команды передаются в форме ключевых слов, а не специальных символов, и указывают на необходимость совершить ту или иную операцию. В табл.1 приведен список ключевых слов (команд), определенный в спецификации SMTP - RFC 821.

Таблица 1. Команды простого протокола передачи почты (SMTP)

Команда

Обязательна

Описание

HELO

Х

Идентифицирует модуль-передатчик для модуля-приемника (hello).

MAIL

Х

Начинает почтовую транзакцию, которая завершается передачей данных в один или несколько почтовых ящиков (mail).

RCPT

Х

Идентифицирует получателя почтового сообщения (recipient).

DATA

Строки, следующие за этой командой, рассматриваются получателем как данные почтового сообщения. В случае SMTP, почтовое сообщение заканчивается комбинацией символов: CRLF-точка-CRLF.

RSET

Прерывает текущую почтовую транзакцию (reset).

NOOP

Требует от получателя не предпринимать никаких действий, а только выдать ответ ОК. Используется главным образом для тестирования.(No operation).

QUIT

Требует выдать ответ ОК и закрыть текущее соединение.

VRFY

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

SEND

Начинает почтовую транзакцию, доставляющую данные на один или несколько терминалов (а не в почтовый ящик).

SOML

Начинает транзакцию MAIL или SEND, доставляющую данные на один или несколько терминалов или в почтовые ящики.

SAML

Начинает транзакцию MAIL и SEND, доставляющие данные на один или несколько терминалов и в почтовые ящики.

EXPN

Команда SMTP-приемнику подтвердить, действительно ли аргумент является адресом почтовой рассылки и если да, вернуть адрес получателя сообщения (expand).

HELP

Команда SMTP-приемнику вернуть сообщение-справку о его командах.

TURN

Команда SMTP-приемнику либо сказать ОК и поменяться ролями, то есть стать STMP- передатчиком, либо послать сообщение-отказ и остаться в роли SMTP-приемника.

Последовательность команд SMTP

Как мы уже отмечали, SMTP обеспечивает двухстороннюю связь между агентами передачи почты (МТА), клиентом и сервером. Клиенты шлют команды серверу, а серверы отвечают клиентам. Однако SMTP оговаривает последовательность SMTP-команд. Лучший способ понять это - взглянуть на образец почтовой транзакции. Следующий пример (он взят целиком из RFC 821) демонстрирует типичную почтовую транзакцию. В примере фигурирует мистер Smith (на компьютере usc.edu), посылающий сообщения мистерам Jones, Green и Brown (на компьютере mit.edu). Агент передачи почты хоста mit.edu принимает почту для мистеров Jones и Brown, однако не знает, где расположен почтовый ящик мистера Green.

Для целей дальнейшего повествования каждой строке присвоен номер и обозначено, кому они принадлежат - передатчику или приемнику. Текст справа от слов <RECEIVER> или <SENDER> содержит действительно передаваемые данные. Трехзначные цифровые комбинации в начале передаваемых строк обозначают коды ответа (их значение объясняется позже). Ответ SMTP похож на сообщения-подтверждения о доставке, поскольку появляется лишь в том случае, когда приемник получил данные.

1

RECEIVER

220 mit.edu Simple Mail Transfer Service Ready

2

SENDER

HELO usc.edu

3

RECEIVER

250 mit.edu

4

SENDER

MAIL FROM: <Smith@usc.edu>

5

RECEIVER

250 OK

6

SENDER

RCPT TO:<Jones@mit.edu>

7

RECEIVER

250 OK

8

SENDER

RCPT TO:<Green@mit.edu>

9

RECEIVER

550 No such user here

10

SENDER

RCPT TO:<Brown@mit.edu>

11

RECEIVER

250 OK

12

SENDER

DATA

13

RECEIVER

354 Start mail input; end with <CRLF>.<CRLF>

14

SENDER

Blah blah blah...

15

SENDER

...etc. etc. etc.

16

SENDER

.

17

RECEIVER

250 OK

18

SENDER

QUIT

19

RECEIVER

221 mit.edu Service closing transmission channel

Как видно из строки 1, когда SMTP-клиент устанавливает TCP-соединение с портом протокола 25, SMTP-сервер отвечает кодом 220. Это означает, что соединение успешно установлено:

1. RECEIVER: 220 mit.edu Simple Mail Transfer Service Ready

После того как MTA компьютеров mit.edu и usc.edu установили соединение и обменялись приветствием, первой командой, согласно спецификации, должна быть команда HELO. Как указано в строке 2, SMTP-клиент передает HELO, указывая имя своего компьютера в качестве аргумента. Другими словами, он сообщает: <Привет, я - usc.edu>. Команда HELO употребляется с аргументом, как показано ниже:

2. SENDER: HELO usc.edu

В ответ на HELO приемник выдает код 250, сообщая передатчику о том, что команда принята и обработана:

3. RECEIVER: 250 mit.edu

После установления TCP-соединения и идентификации (при помощи HELO) SMTP-клиент приступает к почтовой транзакции. Для начала он выполняет одну из следующих команд: MAIL, SEND, SOML или SAML. В нашем примере использована команда MAIL:

4. SENDER: MAIL FROM:<Smith@usc.edu>

Все четыре команды, MAIL, SEND, SOML и SAML, имеют одинаковый синтаксис:

MAIL <пробел> FROM:<reverse-path> <carriage-return line-feed>

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

Аргумент <обратный путь> (reverse path) указывает серверу, кому в случае ошибки отослать соответствующее сообщение. Мы еще рассмотрим его подробнее. На данный момент для нас важно, что в аргументе содержится адрес источника сообщения (в нашем случае, Smith@usc,edu). После того как сервер выдал код ответа 250 (строка 5), согласившись обработать сообщение от Smith@usc.edUt необходимо указать получателя сообщения. Это делается при помощи команды RCPT. Команда RCPT имеет аргумент - имя получателя. На одну команду приходится только одно имя, поэтому, если получателей несколько, команда RCPT выдается несколько раз. В нашем примере команды RCPT выполняются в строках 6, 8 и 10. Синтаксис RCPT похож на синтаксис команды MAIL:

RCPT <пробел> TO:<forward-path> <CRLF>

Однако, в отличие от MAIL, аргумент RCPT начинается со слова <ТО:>. Содержимое аргумента - путь передачи сообщения (forward path), а не обратный путь. На данный момент для нас важно, что в пути передачи сообщения указано имя почтового ящика получателя. Выдав команду RCPT, МТА-клиент ожидает получить ответ с кодом 250. Однако в ответ на восьмую строку

8. SENDER: RCPT TO:<Green@mit.edu>

сервер отвечает кодом 550:

9. RECEIVER: 550 No such user here

Код ответа 550 означает, что МТА не в состоянии выполнить запрос клиента, поскольку не знает, как доставить почту указанному пользователю. То есть, скорее всего, у мистера по фамилии Green нет почтового ящика (Green@mit,edu) на этом компьютере. В протоколе SMTP сказано, что сервер обязан информировать клиента об отсутствии почтового ящика получателя сообщения. Однако в спецификации SMTP ничего не говорится о том, как клиент должен реагировать на это сообщение.

После того как посланы все команды RCPT, клиент начинает передачу данных при помощи команды DATA. В строке 12 показано, как МТА-клиент (передатчик) высылает команду DATA, в строке 13 - как сервер отвечает кодом 354. Этот код означает, что передача данных разрешена и должна заканчиваться комбинацией CRLF-<точка>-CRLF (новой строкой, содержащей только точку).

12. SENDER: DATA
13. RECEIVER: 354 Start mail input; end with <CRLF>.<CRLF>

После того как получен код 354, клиент может начать передачу данных. МТА-сервер, в свою очередь, помещает принятые данные в очереди входящих сообщений. Сервер не высылает никаких ответов до тех пор, пока не получит комбинацию CRLF-точка-CRLF от клиента, означающую конец передачи данных. Как показано в строках 16 и 17, в ответ на полученную комбинацию CRLF-<точка>-CRLF, сервер выдает код 250. Как мы уже говорили, код ответа 250 означает успешное окончание операции:

16. SENDER: .
17. RECEIVER: 250 OK

Для того чтобы закончить почтовую транзакцию, клиент, по правилам SMTP, обязан послать команду QUIT. Сервер, в свою очередь, отвечает кодом 221. Этот код подтверждает клиенту, что соединение будет закрыто, после чего соединение действительно закрывается:

18. SENDER: QUIT
19. RECEIVER: 221 mit.edu Service closing transmission channel

В любой момент во время транзакции клиент может использовать команды NOOP, HELP, EXPN и VRFY. В ответ на каждую команду сервер высылает клиенту определенную информацию. Конечно, в зависимости от ответа клиент может предпринять определенные действия, однако спецификация SMTP ничего не говорит по этому поводу. Например, клиент-МТА может передать команду VRFY для того, чтобы убедиться, что имя пользователя действительно. Если сервер ответит, что данного имени не существует, клиент МТА может не передавать почту для этого пользователя. В спецификации SMTP, однако, на этот счет нет никаких указаний - клиент может ничего не делать в ответ на команду VRFY. МТА-клиент может ничего не делать также в ответ на команды NOOPHELP и EXPN - ответственность целиком лежит на разработчике конкретной реализации МТА.

Коды ответов SMTP

В спецификации SMTP требуется, чтобы сервер отвечал на каждую команду SMTP-клиента. МТА-сервер отвечает трехзначной комбинацией цифр, называемой кодом ответа. Вместе с кодом ответа, как правило, передается одна или несколько строк текстовой информации.

Примечание: Несколько строк текста, как правило, сопровождают только команды EXPN и HELP.В спецификации SMTP, однако, ответ на любую команду может состоять из нескольких строк текста.

Каждая цифра в коде ответа имеет определенный смысл. Первая цифра означает, было ли выполнение команды успешно (2), неуспешно (5) или еще не закончилось (3). Как указано в приложении Е документа RFC 821, простой SMTP-клиент может анализировать только первую цифру в ответе сервера, и на основании ее продолжать свои действия. Вторая и третья цифры кода ответа разъясняют значение первой. Если вы разрабатываете SMTP-приложение, обязательно изучите конструкцию всех кодов SMTP-ответа. То, как коды составлены в самом SMTP - превосходный образец грамотного подхода к делу. В табл.2 приведены возможные значения кодов ответа SMTP, определенные в RFC 821.

Таблица 2. Коды ответа SMTP и их значение

Код

Значение

211

Ответ о состоянии системы или помощь.

214

Сообщение-подсказка (помощь)

220

<имя_домена> служба готова к работе

221

<имя_домена> служба закрывает канал связи

250

Запрошенное действие почтовой транзакции успешно завершилось

251

Данный адресат не является местным; сообщение будет передано по маршруту <forward-path>

354

Начинай передачу сообщения. Сообщение заканчивается комбинацией CRLF-точка-CRLF

421

<имя_домена> служба недоступна; соединение закрывается

450

Запрошенная команда почтовой транзакции не выполнена, так как почтовый ящик недоступен

451

Запрошенная команда не выполнена; произошла локальная ошибка при обработке сообщения

452

Запрошенная команда не выполнена; системе не хватило ресурсов

500

Синтаксическая ошибка в тексте команды; команда не опознана

501

Синтаксическая ошибка в аргументах или параметрах команды

502

Данная команда не реализована

503

Неверная последовательность команд

504

У данной команды не может быть аргументов

550

Запрошенная команда не выполнена, так как почтовый ящик недоступен

551

Данный адресат не является местным; попробуйте передать сообщение по маршруту <forward-path>

552

Запрошенная команда почтовой транзакции прервана; дисковое пространство, доступное системе, переполнилось

553

Запрошенная команда не выполнена; указано недопустимое имя почтового ящика

554

Транзакция не выполнена

Промежуточные агенты

Термин "маршрут доставки" (forward-path) служит для того, чтобы отличать почтовый ящик (mailbox), имя которого абсолютно, от пути (он может быть различным), по которому следует почта. Предположим, что мы хотим доставить два почтовых сообщения на один и тот же сетевой компьютер. Оба сообщения имеют один и тот же адрес, однако не обязательно будут следовать по одному и тому же маршруту. Точно так же, если на пришедшие сообщения выдаются ответы, они не обязательно будут следовать по указанному обратному маршруту (reverse-path). Как правило, конкретный маршрут для почты выбирается системным администратором. Чтобы направить почту по нужному пути, используются значения маршрута доставки и обратного маршрута, в которых указываются промежуточные агенты (relay agents). Промежуточный агент доставки - это МТА, так называемый почтовый хаб (mail hub), настроенный на передачу транзитной почты. Чтобы доставить сообщение, местный агент пользователя (UA) передает его местному МТА, который, в свою очередь, передает его промежуточному агенту МТА. В следующем примере Smith@usc.edu является почтовым ящиком, a HOSTI, HOST2 и HOST3 - промежуточными агентами:

MAIL FROM:<@HOSTI, @HOST2, @HOST3:Smith@usc.edu>

В наше время промежуточные агенты присутствуют практически во всех сетях, входящих в Internet. На рис.6 приведена типичная конфигурация почтовой системы Internet с участием промежуточных агентов.

http://www2.icmm.ru/~masich/win/lexion/mail/mailfig6.gif

Чтобы упростить процесс конфигурации почтовой системы, в локальной сети устанавливается один компьютер, служащий промежуточным агентом (relay host). Вся почта пользователей попадает сначала на него. Затем этот компьютер рассылает сообщения по Internet. Кроме всего прочего, такой компьютер может служить защитой фирмы от взломщиков-хакеров из Internet. Ограничивая общение локальной сети с внешним миром до уровня почты, организация сводит до минимума риск нежелательного вторжения в свои собственные системы.

Кроме того, администрировать и защищать в этом случае приходится единственный компьютер. SMTP в состоянии послать сообщение непосредственно с компьютера пользователя на компьютер адресата в том случае, если между ними существует прямое почтовое соединение. К сожалению, это далеко не всегда так. Как правило, между двумя компьютерами находится один или несколько промежуточных агентов. Чтобы обеспечить доставку, в почтовом сообщении нужно указать имя компьютера-получателя и точное наименование почтового ящика. Аргументом команды MAIL является обратный маршрут, включающий имя источника сообщения и имена всех промежуточных агентов. Аргумент команды RCPT - маршрут доставки, содержащий имя получателя сообщения. Обратный маршрут описывает путь, который прошло сообщение, тогда как маршрут доставки идентифицирует место назначения. Обратный маршрут используется SMTP, когда нужно передать сообщение о случившейся ошибке или о невозможности доставить сообщение, когда оно уже прошло через промежуточный агент. По мере продвижения сообщения по Internet записи о его маршрутах изменяются. В обязанности системных администраторов входит правильно настраивать местные МТА на передачу сообщений промежуточному агенту, и наоборот, промежуточные агенты на доставку сообщений местным МТА. Если у промежуточного МТА изменится имя, все, что нужно сделать в конфигурации местного МТА - изменить имя компьютера в системе DNS. Другие параметры конфигурации не изменяются. Другими словами, повторим еще раз, что иметь один компьютер для промежуточной доставки - значит, снять с себя значительную часть головной боли по настройке почтовой системы - ведь придется заботиться только об одном компьютере.

Рассмотрим почтовую транзакцию между промежуточными агентами SMTP. До того как сообщение будет передано следующему указанному в маршруте (в поле ТО:) компьютеру, имя данного компьютера удаляется из маршрута доставки и добавляется в начало обратного маршрута. К тому моменту, когда сообщение достигнет пункта назначения, маршрут доставки будет содержать только имя почтового ящика. В RFC 821 приведен пример того, как изменяется содержимое маршрутов по мере обработки почтового сообщения. Когда промежуточный агент А получает почту со следующими аргументами:

FROM: <USERX@HOSTY.ARPA>
TO: <@HOSTA.ARPA, @HOSTB.ARPA: USERC@HOSTD.ARPA>

он переправляет почту сетевому компьютеру В со следующими аргументами:

FROM: <@HOSTA.ARPA: USERX@HOSTY.ARPA> TO: <@HOSTB.ARPA: USERC@HOSTD.ARPA> .

Как видим, промежуточный агент A (HOSTA.ARPA) убрал свое имя из заголовка <ТО:> и добавил в заголовок <FROM:>. Промежуточный агент компьютера В совершит аналогичное действие, и следующим пунктом назначения сообщения будет почтовый ящик USERC на компьютере HOSTD.ARPA.

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

2.5. Разработанные меню и интерфейсы

При запуске программы на экране появляется главное окно программы с элементами управления (Рис. 2.1).

http://works.doklad.ru/images/t3I4diZshXQ/m1ac45d6d.jpg

Рис. 2.1. «Главное окно»

Для настройки почтового клиента необходимо нажать на меню «Настройки». В результате, загружается окно настроек (рис. 2.2.).

http://works.doklad.ru/images/t3I4diZshXQ/4b3b9a4c.jpg

Рис. 2.2. «Настройки»

Для отправки письма необходимо запустить окно «Написать письмо» (рис 2.3.), нажатием на кнопку «Написать письмо» в меню «Файл».

http://works.doklad.ru/images/t3I4diZshXQ/m2ac883e6.jpg

Рис. 2.3. окно «Написать сообщение»

UDP — User Datagram Protocol

В отличие от TCP UDP — очень быстрый протокол, поскольку в нем определен самый минимальный механизм, необходимый для передачи данных. Конечно, он имеет некоторые недостатки. Сообщения поступают в любом порядке, и то, которое отправлено первым, может быть получено последним. Доставка сообщений UDP вовсе не гарантируется, сообщение может потеряться, и могут быть получены две копии одного и того же сообщения. Последний случай возникает, если для отправки сообщений в один адрес использовать два разных маршрута.

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

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

Однонаправленное (unicast) сообщение отправляется из одного узла только в один другой узел. Это также называется связью "точка-точка". Протокол TCP поддерживает лишь однонаправленную связь. Если серверу нужно с помощью TCP взаимодействовать с несколькими клиентами, каждый клиент должен установить соединение, поскольку сообщения могут отправляться только одиночным узлам.

Широковещательная передача (broadcast) означает, что сообщение отправляется всем узлам сети. Групповая рассылка (multicast)- это промежуточный механизм: сообщения отправляются выбранным группам узлов.

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

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

Заголовок UDP гораздо короче и проще заголовка TCP:

Заголовок UDP.

Поле

Длина

Описание

Порт источника

2 байта

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

Порт назначения

2 байта

Номер почты назначения

Длина

2 байта

Длина сообщения, включая заголовок и данные.

Контрольная сумма

2 байта

Контрольная сумма заголовка и данных для проверки

UDP – это быстрый протокол, не генерирующий доставки. Если требуется поддержание порядка сообщений и надёжная доставка, нужно использовать TCP. UDP главным образом предназначен для широковещательного и групповой передачи. Протокол UDP определён в RFC 786.

Разработка приложения

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

1. Этап проектирования интерфейса программы;

2. Этап написание программного кода;

3. Этап доработки, внесения изменений;

4. Этап формирования конечного продукта.

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

Этап задания обучающего материала включает в себя:

· Создание определенного макета программы;

· Создание интерфейса управления;

· Разработка оформления.

В результате проектирования выше изложенной части работы программный продукт должен поддерживать следующие функции:

· Ввод текстовой информации и ее изменение до отправки;

· Быстродействие и удобство пользования интерфейсом.

Этап написания программного кода:

· Выбор и анализ методов написания алгоритма выполнения программы;

· Написание основных функций;

· Внесение исправлений и настройка программы.

Описание программы сервера локальный чат сервер программный

Программа выполняет следующие функции:

· Контролирует подключение пользователей и их количество;

· Получает сообщения от каждого пользователя;

· Передаёт сообщения от одного пользователя в общий чат.

В главной функции сервера задаются настройки сервера, принятие и отправка сообщений всем пользователям чата. Помимо этого, ведется протоколирование всего происходящего и выводится на консоль (Рис. 1).

Рис. 1

Настройки сервера:

server.ai_family = AF_INET;

server.ai_flags = AI_PASSIVE;

server.ai_socktype = SOCK_STREAM;

server.ai_protocol = IPPROTO_TCP;

getaddrinfo(NULL, "8888", &server, &result);

8888 - номер порта, с которым будет работать чат.

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

CreateThread(NULL, NULL, (PTHREAD_START_ROUTINE)SendMessageToClient, (LPVOID)(ClientCount - 1), NULL, NULL);

Слушающий сокет:

Listen = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

bind(Listen, result->ai_addr, result->ai_addrlen);

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

void SendMessageToClient(int ID)

{

char* buffer = new char[1024];

for (;; Sleep(100))

{

memset(buffer, 0, sizeof(buffer));

if (recv(Connections[ID], buffer, 1024, NULL))

{

printf("%s", buffer);

for (int i = 0; i <= ClientCount; i++)

{

send(Connections[i], buffer, strlen(buffer), NULL);

}

}

}

free(buffer);

}

Описание программы клиента

При запуске программы клиента отображается окно, в котором необходимо заполнить два поля. Первое из них нужно для соединения с сервером и требует правильного заполнения, второе нужно для инициализации пользователя и может быть заполнено по усмотрению самого пользователя (Рис. 2).

Рис. 2

Помимо этого, в этом же окне присутствует CheckBox, который восстанавливает значение IP адреса, введенного в прошлый раз, тем самым облегчая ввод. (Рис. 3)

Рис. 3

Ниже приведена реализация данной функции:

private void checkBox1_CheckedChanged(object sender, EventArgs e)

{

try

{

if (checkBox1.Checked)

{

var sr = new StreamReader(@"Chat_Info/nastroyki.txt");

string buffer = sr.ReadToEnd();

sr.Close();

string[] ip = buffer.Split(':');

textBox1.Text = ip[0];

checkBox1.Enabled = false;

}

}

catch (Exception ex)

{

MessageBox.Show("Ошибка: " + ex.Message);

}

}

Так же в правом нижнем углу есть изображение, в виде знака вопроса, при нажатии на которое отображается инструкция для пользователя. (Рис. 4)

Рис. 4

В нижней части окна находится кнопка «Войти в чат!», при нажатии на которую закрывается данное окно и открывается новое, в котором происходит подключении к серверу по заданным ранее настройкам.

private void button1_Click(object sender, EventArgs e)

{

if (textBox1.Text != " " && textBox1.Text != "" && textBox2.Text != " " && textBox2.Text != "")

{

try

{

DirectoryInfo data = new DirectoryInfo("Chat_Info");

data.Create();

var sw = new StreamWriter(@"Chat_Info/nastroyki.txt");

sw.WriteLine(textBox1.Text + ":" + textBox2.Text);

sw.Close();

this.Hide();

Form1 form = new Form1();

form.Show();

}

catch (Exception ex)

{

MessageBox.Show("Ошибка: " + ex.Message);

}

}

else MessageBox.Show("Заполните все поля!");

}

При этом, если оба или одно из полей осталось пустым, на экран будет выведено соответствующее сообщение. (Рис. 5)

Рис. 5

Если IP адрес сервера был введён корректно, то в окне приложения зелёным цветом будет отображаться именно этот IP адрес (Рис. 6).

Рис. 6

В противном случае, будет выведена надпись об отсутствии настроек и появится кнопка, с помощью которой можно заново ввести настройки (Рис. 7).

Рис. 7

Если выведена зелёная надпись IP адреса и в листинге чата написано «Connect…», соответственно пользователь успешно подключился и может начать писать своё сообщение в нижнем поле для ввода текста. Для отправки сообщения нужно нажать кнопку «Отправить» либо нажать кнопку «Enter» на клавиатуре.

private void button1_Click_1(object sender, EventArgs e)

{

SendMessage("\n" + nik + ": " + textBox2.Text + "**8");

textBox2.Clear();

}

В Меню приложения есть кнопка «Автор», при нажатии на которую выводится информация о разработчике приложения (Рис. 8).

Рис. 8

Для выхода из чата нужно выбрать пункт из меню «Выход», после чего в листинге чата, пользователь будет проинформирован о том, что он вышел из чата (Рис. 9). При этом, если он выбрал этот пункт меню до того, как вошел в чат, то также будет выведено соответствующее сообщение. (Рис. 7)

Рис. 9

private void выходToolStripMenuItem_Click_1(object sender, EventArgs e)

{

if (th != null) th.Abort();

if (Client != null)

{

Client.Close();

textBox2.Enabled = false;

button1.Enabled = false;

label5.Enabled = false;

listBox1.Items.Add("Вы вышли из чата...");

}

else listBox1.Items.Add("Вы ещё не зашли в чат!");

}

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

void SendMessage(string message)

{

if (message != " " && message != "")

{

byte[] buffer = new byte[1024];

buffer = Encoding.UTF8.GetBytes(message);

Client.Send(buffer);

}

}

Функция приёма сообщений от сервера:

void RegOfMessage()

{

byte[] buffer = new byte[1024];

for (int i = 0; i < buffer.Length; i++)

{ buffer[i] = 0; }

for (; ; )

{

try

{

Client.Receive(buffer);

string messege = Encoding.UTF8.GetString(buffer);

int count = messege.IndexOf("**8");

if (count == -1)

{ continue; }

string Clean_Mes = "";

for (int i = 0; i < count; i++)

{ Clean_Mes += messege[i]; }

for (int i = 0; i < buffer.Length; i++)

{ buffer[i] = 0; }

this.Invoke((MethodInvoker)delegate()

{ listBox1.Items.Add(Clean_Mes); });

}

catch (Exception ex) { }

}

}

Подключение клиента реализуется следующим образом:

Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

if (ip != null)

{

Client.Connect(ip, port);

th = new Thread(delegate() { RegOfMessage(); });

th.Start();

}

Приложение

My_Server.cpp:

#pragma comment(lib,"Ws2_32.lib")

#include <WinSock2.h>

#include <iostream>

#include <WS2tcpip.h>

SOCKET Connect;

SOCKET* Connections;

SOCKET Listen;

int ClientCount = 0;

void SendMessageToClient(int ID)

{

char* buffer = new char[1024];

for (;; Sleep(100))

{

memset(buffer, 0, sizeof(buffer));

if (recv(Connections[ID], buffer, 1024, NULL))

{

printf("%s", buffer);

for (int i = 0; i <= ClientCount; i++)

{

send(Connections[i], buffer, strlen(buffer), NULL);

}

}

}

free(buffer);

}

int main()

{

setlocale(LC_ALL, "russian");

WSAData data;

WORD version = MAKEWORD(2, 2);

int res = WSAStartup(version, &data);

if (res != 0)

{

return 0;

}

struct addrinfo server;

struct addrinfo * result;

Connections = (SOCKET*)calloc(64, sizeof(SOCKET));

ZeroMemory(&server, sizeof(server));

server.ai_family = AF_INET;

server.ai_flags = AI_PASSIVE;

server.ai_socktype = SOCK_STREAM;

server.ai_protocol = IPPROTO_TCP;

getaddrinfo(NULL, "8888", &server, &result);

Listen = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

bind(Listen, result->ai_addr, result->ai_addrlen);

listen(Listen, SOMAXCONN);

freeaddrinfo(result);

char conect[] = "Connect...**8";

char kol[] = "All users: ";

printf("Server connect...\n");

printf(kol);

printf("%d", ClientCount);

for (;; Sleep(100))

{

if (Connect = accept(Listen, NULL, NULL))

{

printf("\nClient connect...\n");

printf(kol);

printf("%d", ClientCount + 1);

Connections[ClientCount] = Connect;

send(Connections[ClientCount], conect, strlen(conect), NULL);

ClientCount++;

CreateThread(NULL, NULL, (PTHREAD_START_ROUTINE)SendMessageToClient, (LPVOID)(ClientCount - 1), NULL, NULL);

}

}

return 1;

}

MyChat_Client

Form1.cs:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

using System.Net.Sockets;

using System.IO;

using System.Net;

using System.Threading;

namespace MyChat_Client

{

public partial class Form1 : Form

{

static private Socket Client;

private IPAddress ip = null;

private int port = 8888;

private string nik;

private Thread th;

public Form1()

{

InitializeComponent();

textBox2.Enabled = false;

button1.Enabled = false;

label5.Enabled = false;

try

{

var sr = new StreamReader(@"Chat_Info/nastroyki.txt");

string buffer = sr.ReadToEnd();

sr.Close();

string[] info = buffer.Split(':');

ip = IPAddress.Parse(info[0]);

nik = info[1];

label4.ForeColor = Color.Green;

label4.Text = " Подключение по IP сервера: " + info[0];

Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

if (ip != null)

{

Client.Connect(ip, port);

th = new Thread(delegate() { RegOfMessage(); });

th.Start();

button1.Enabled = true;

textBox2.Enabled = true;

label5.Enabled = true;

listBox1.Items.Add(nik + " присоединился!");

}

}

catch (Exception ex)

{

label4.ForeColor = Color.Red;

label4.Text = "Настройки не найдены\nВведите заново!";

button2.Visible = true;

}

}

void SendMessage(string message)

{

if (message != " " && message != "")

{

byte[] buffer = new byte[1024];

buffer = Encoding.UTF8.GetBytes(message);

Client.Send(buffer);

}

}

void RegOfMessage()

{

byte[] buffer = new byte[1024];

for (int i = 0; i < buffer.Length; i++)

{ buffer[i] = 0; }

for (; ; )

{

try

{

Client.Receive(buffer);

string messege = Encoding.UTF8.GetString(buffer);

int count = messege.IndexOf("**8");

if (count == -1)

{ continue; }

string Clean_Mes = "";

for (int i = 0; i < count; i++)

{ Clean_Mes += messege[i]; }

for (int i = 0; i < buffer.Length; i++)

{

buffer[i] = 0;

}

this.Invoke((MethodInvoker)delegate()

{

listBox1.Items.Add(Clean_Mes);

});

}

catch (Exception ex) { }

}

}

private void авторToolStripMenuItem_Click_1(object sender, EventArgs e)

{

MessageBox.Show("Булычёв Вячеслав \n ДБО - 481 \n 2015г");

}

private void button1_Click_1(object sender, EventArgs e)

{

SendMessage("\n" + nik + ": " + textBox2.Text + "**8");

textBox2.Clear();

}

private void textBox2_KeyDown(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.Enter)

{

SendMessage("\n" + nik + ": " + textBox2.Text + "**8");

textBox2.Clear();

}?

}

private void Form1_Load(object sender, EventArgs e)

{

}

private void button2_Click(object sender, EventArgs e)

{

this.Hide();

Form3 form = new Form3();

form.Show();

}

private void выходToolStripMenuItem_Click_1(object sender, EventArgs e)

{

if (th != null) th.Abort();

if (Client != null)

{

Client.Close();

textBox2.Enabled = false;

button1.Enabled = false;

label5.Enabled = false;

listBox1.Items.Add("Вы вышли из чата...");

}

else listBox1.Items.Add("Вы ещё не зашли в чат!");

}

}

}

Form3.cs:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.IO;

using System.Windows.Forms;

namespace MyChat_Client

{

public partial class Form3 : Form

{

public Form3()

{

InitializeComponent();

}

private void button1_Click(object sender, EventArgs e)

{

if (textBox1.Text != " " && textBox1.Text != "" && textBox2.Text != " " && textBox2.Text != "")

{

try

{

DirectoryInfo data = new DirectoryInfo("Chat_Info");

data.Create();

var sw = new StreamWriter(@"Chat_Info/nastroyki.txt");

sw.WriteLine(textBox1.Text + ":" + textBox2.Text);

sw.Close();

this.Hide();

Form1 form = new Form1();

form.Show();

}

catch (Exception ex)

{

MessageBox.Show("Ошибка: " + ex.Message);

}

}

else MessageBox.Show("Заполните все поля!");

}

private void pictureBox1_Click(object sender, EventArgs e)

{

MessageBox.Show("Для начала общения с другими пользователями Вам необходимо ввести IP сервера и свой Ник, нажать кнопку ' Войти в чат! ' или кнопку ' Enter ', после чего вы сможете обмениваться сообщениями. Для выхода из чата нужно выбрать пункт ' Выход ' в меню, либо закрыть приложение");

}

private void textBox2_KeyDown(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.Enter)

{

if (textBox1.Text != " " && textBox1.Text != "" && textBox2.Text != " " && textBox2.Text != "")

{

try

{

DirectoryInfo data = new DirectoryInfo("Chat_Info");

data.Create();

var sw = new StreamWriter(@"Chat_Info/nastroyki.txt");

sw.WriteLine(textBox1.Text + ":" + textBox2.Text);

sw.Close();

this.Hide();

Form1 form = new Form1();

form.Show();

}

catch (Exception ex)

{

MessageBox.Show("Ошибка: " + ex.Message);

}

}

else MessageBox.Show("Заполните все поля!");

}?

}

private void checkBox1_CheckedChanged(object sender, EventArgs e)

{

try

{

if (checkBox1.Checked)

{

var sr = new StreamReader(@"Chat_Info/nastroyki.txt");

string buffer = sr.ReadToEnd();

sr.Close();

string[] ip = buffer.Split(':');

textBox1.Text = ip[0];

checkBox1.Enabled = false;

}

}

catch (Exception ex)

{

MessageBox.Show("Ошибка: " + ex.Message);

}

}

}

}

Заключение

Используемая литература.

Лабор В.В «Создание приложений для Windows», 2003 г.

http://www2.icmm.ru/~masich/win/lexion/mail/smtp.html#model

https://professorweb.ru/my/csharp/web/level1/1_4.php

https://ru.wikipedia.org/wiki/