Каталог статей

Главная » Статьи » C/С++

Практическое использование классов .NET Framework для разработки «корпоративного мессенджера»
Преамбула

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

«Добрый день! 

вот ТЗ, посмотрите 

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

вот на менеджерской и должно быть право писать сообщения. После того, как сообщения написаны, они отправляются на сервер. Там они сохраняются. 

Клиентская часть - она простенькая, она должна уметь обрабатывать ту новую инфу которая есть на сервере. Клиентская часть будет стоять у всех рядовых клиентов (менеджерская - у администрации). Вход в клиентскую часть должен осуществляться по личным данным (логин и пароль), эти данные должны устанавливаться только в менеджерской части (т.е. администратор должен регистрирвоать новых клиентов и также удалять их, чтобы самовольно никто не смог получить доступ)...так вот, те люди которые вошли по свои данным в клиентскую часть, сидят в on-line (а программа тихонько работает в трее)...и если программа обнаруживает новую инфу на сервер, она её автоматически обрабатывает и выкидывает на экран и человек её читает.... 

ну вот примерно и всё....небольшие моменты: 

-хотелось бы чтобы приход новой инфы соправождался звуком. 

-чтобы в менеджерской части видно было, кто в он-лайне, а кто нет (если это сильно сложно, ну тогда не нужно) 

... 

ну и конечно - программа должна быть на русском. вот и всё ТЗ... 

...» 
Введение

Сразу отвечать на письмо согласием или отказом я не стал, решил для начала немного подумать о возможных способах решения данной задачи, после чего и ответить, а подумать было над чем, и, пожалуй, самое главное, то, что тема данной работы для меня новая, даже, я бы сказал, непривычная. Так как вся моя работа в IT, на протяжении почти 10 лет, была связана с БД, а в предложенной задаче, невооруженным глазом была видна необходимость использования socket и multithreading, то браться за нее было бы довольно рискованное мероприятие. Но с другой стороны, в последнее время меня очень интересует .NET Framework и язык C#, а что бы изучить что-то новое надо практиковаться и практиковаться. И, подумав еще пару дней (предварительно, набросав для себя некоторые технические решения), я дал согласие, с условием, что работу буду делать практически бесплатно, но на C# (ответа на свое предложение, так и не получил). 
Анализ требований

Выделим из письма, то, что можно было бы назвать требованиями: 

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

- "Вход в клиентскую часть должен осуществляться по личным данным (логин и пароль), ... регистрирвоать новых клиентов ...". 

-чтобы в менеджерской части видно было, кто в он-лайне, а кто нет (если это сильно сложно, ну тогда не нужно) 

ну и конечно - программа должна быть на русском. вот и всё ТЗ... 

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

Пользователи системы. 

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

В общем, после обдумывания, получается примерно следующее: у каждого пользователя будет уровень доступа: ‘r’ - читатель (рядовой пользователь), ‘w’ - писатель (менеджер). И, что самое интересное, у пользователя может быть владелец (тот самый графоманствующий сержант), а может даже и не один владелец, добавлю что, ничего не мешает тому, что бы, например, два менеджера были прописаны владельцами друг у друга, и тогда они смогут общаться между собой. 

Сама, в смысле система. 

Действительно будет две части, серверная и клиентская: 

функциональность серверной части: 
хранение списка пользователей (интерфейс для регистрации новых пользователей, удаления или редактирования существующих); 
хранение истории сообщений (интерфейс для просмотра); 
аутентификация имени пользователя и пароля; 
перенаправление сообщений от одной группы пользователей ("писатель") к другой группе пользователей (могут быть и «читатели» и «писатели»), в соответствии с условиями владения (наверное, помните, что у каждого пользователя может быть владелец, который есть ничто иное, как другой пользователь, только более шустрый). 

функциональность клиентской части: 
для группы пользователей "писатель"; 
отправлять введенные сообщения (предоставлять соответствующий пользовательский интерфейс для ввода сообщений) серверной части; 
отображать список пользователей "читатель" и их статус (в сети, не в сети); 
отображать полученную информацию. 
для группы пользователей "читатель"; 
отображать полученную информацию. 

На первый взгляд, кажется, что реализовать все это, легко и просто, но это только на первый взгляд. Дальше сами увидите и поймете, кстати, первую версию (точнее 0.97) можно скачать. 

Приведу ряд диаграмм последовательностей (sequence diagram) взаимодействий клиентской и серверной части между собой, и между потоками. Диаграммы облегчат понимание того, что собственно должно быть сделано в рамках данного проекта.

                                          рис .1 Sequence diagram – вход в систему


                         рис .2 Sequence diagram – отправка сообщений с клиента

рис .3 Sequence diagram – завершение работы пользователя с клиентом корпоративного мессенджера

                             рис .4 Sequence diagram – проверка наличия пользователя в сети

Проектирование и кодирование
На диаграммах, можно выделить наличие следующих элементов, распишем их сразу по принадлежности к серверной или клиентской части разрабатываемого приложения: 
MSGClient - клиентская часть, состоит: 
Graphic User Interface (GUI), который, реагирует на действия пользователя и на действия со стороны SocketThreadClient; 
SocketThreadClient - поток, обрабатывающий сообщения приходящие по локальной сети от MSGServer; 
MSGServer - серверная часть, состоит: 
Graphic User Interface (GUI), который, реагирует на действия пользователя и на действия со стороны SocketThreadServer, MessageThreadServer, Timer; 
SocketThreadServer - поток, обрабатывающий сообщения приходящие по локальной сети от MSGClient (которых может быть много); 
MessageThreadServer - поток, обрабатывающий очередь сообщений; 
Timer – который, будет провоцировать, время от времени, проверку наличия пользователей в сети (что бы ни слать лишний раз им информацию – все равно ведь не получат). 

Что бы ни быть, что называется голословным, приведу часть исходного кода (на C#), реализующего SocketThread: 
  public class CSocketThread
  {

  public UserStatusChange onUserStatusOnline;
  public UserStatusChange onUserStatusOffline;
  public UserMessage onUserMessage;
  public UserLogon onUserLogon;

  public Control guiControlSender;

  public bool SocketTerminate;

  public Queue clients;

  public TcpListener listener;

  public Thread threadAcceptTcpClient;
  public Thread threadAnalysTcpClient;

  public virtual void AcceptTcpClient()
  {
  Console.WriteLine("Waiting for a new connection... ");
  while (!SocketTerminate)
  {
  try
  {
  TcpClient client = null;
  while (!listener.Pending() && !SocketTerminate) 
  Thread.Sleep(100);
  if (!SocketTerminate)
  {
  client = listener.AcceptTcpClient(); 
  if (client != null)
  lock (clients)
  {
  clients.Enqueue(client);
  Console.WriteLine("Connected!");
  Console.WriteLine("Waiting for a new connection... ");
  }
  }
  }
  catch (System.Exception ex)
  {
  Console.WriteLine( ex.Message );
  }
  }
  } // AcceptTcpClient

  public virtual void AnalysTcpClient()
  {
  Console.WriteLine("Waiting a new TCPClient ...");
  while (!SocketTerminate)
  {
  try
  {
  if (clients.Count > 0)
  {
  TcpClient client = null;
  lock (clients)
  {
  client = (TcpClient)clients.Dequeue();
  }
  if ( client != null)
  {
  Console.WriteLine("Analysis TCPClient ...");
  Analysis(client);
  Console.WriteLine("Waiting a new TCPClient ...");
  }
  }
  }
  catch (System.Exception ex)
  {
  Console.WriteLine( ex.Message );
  }
  }
  } // AnalysTcpClient

  public CSocketThread ()
  {
  SocketTerminate = false;
  clients = new Queue();
  }

  public virtual void SocketThreadStart (Control ControlSender,
  string portn,
  string ipaddress)
  {
  guiControlSender = ControlSender;

  Int32 port = Convert.ToInt32( portn );
  IPAddress localAddr = IPAddress.Parse( ipaddress );

  Console.WriteLine("Start listener ...");

  listener = new TcpListener(localAddr, port);
  listener.Start();

  Console.WriteLine("Initializing socket threads ...");
  threadAcceptTcpClient = new Thread(new ThreadStart(AcceptTcpClient));
  threadAnalysTcpClient = new Thread(new ThreadStart(AnalysTcpClient));

  threadAcceptTcpClient.Start();
  threadAnalysTcpClient.Start();
  } // SocketThreadStart

  public virtual void Analysis(TcpClient client)
  {
  } // Analysis

  }
Примечание

Сразу оговорюсь, что данный код демонстрирует, мягко говоря, не совсем корректную работу. Лучше всего, для аналогичной работы, использовать асинхронные методы класса Socket. Но в данной ситуации, только для проверки, отработки некоторых принципов разработки таких систем, я, думаю, что можно пойти и таким, не очень «красивым», путем. 

Что бы понять как поток GUI, получает эстафетную палочку, давайте взглянем, на еще одну интересную часть кода, точнее, часть того кода, что был реализован в Analysis для класса CSocketThreadClient (наследника класса CSocketThread): 
  public override void Analysis(TcpClient client)
  {
  try
  {
  NetworkStream stream = client.GetStream();
  if (stream != null)
  {
  // пытаемся получить сообщение
  byte[] data = new Byte[256];
  String responseData = String.Empty;
  string message = "";
  do
  {
  int bytes = stream.Read(data, 0, data.Length); 
  message = 
  String.Concat(message, Encoding.ASCII.GetString(data, 0, bytes)); 
  }
  while((stream.DataAvailable) && (!SocketTerminate));
  Console.WriteLine("Analyze :: read " + message);
  // разбор сообщений
  if (SocketTerminate)
  return;

  int iPosNotify = message.IndexOf(CMSGNotifyOnlineClient);
  if (iPosNotify == 0) // да, похоже на запрос пользователь on-line
  {
  // имя пользователя
  message = message.Remove(0, CMSGNotifyOnlineClient.Length );
  message = message.Remove(0, CMSGUsername.Length );
  string username = message.Substring(0, CMSGUsers.CLengthUsername);
  username = username.Trim();
  message = message.Remove(0, CMSGUsers.CLengthUsername );
  // делегируем в GUI
  if (onUserStatusOnline != null)
  guiControlSender.BeginInvoke(onUserStatusOnline,
  new object[]{username});
  return;
  }

  } // stream
  }
  catch (SocketException e) 
  {
  Console.WriteLine("SocketException: {0}", e);
  }
  finally
  {
  client.Close();
  }
  } // Analysis

По коду видно, каким образом метод Analysis отличает, один тип сообщения от другого (приведенные ранее диаграммы последовательностей и соответствуют типам сообщений между MSGClient и MSGServer). Также видно, что все данные передаются в обычной строке, которая затем разбирается на составляющие части, которые, в свою очередь передаются дальше. А, метод BeginInvoke и реализует то, что можно назвать передачей эстафетной палочки в GUI-поток. 

Примечание: Особенно интересно наблюдать в отладчике, как после вызова данного метода BeginInvoke, отладка идет одновременно в двух местах. Продолжается работа в методе Analysis, и в то же время идет работа в onUserStatusOnline. И отладчик, мотается туда сюда, ну и я, вместе с ним. 

Реализовав приведенным способом, анализ принимаемой строки, я понял, что есть метод интересней. А, именно, если серверная и клиентская часть, будут обмениваться не просто строками, а строками, содержащими xml, все будет намного проще и красивей, но в таком случае похоже, что вырастет объем трафика. 

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

Первая моя мысль об использовании какой-либо БД, например MS SQL Server, для хранения списка пользователей и истории сообщений, я отмел почти сразу. Почти, потому что, все-таки набросал схему будущей БД в Enterprise Manager, и уже потом понял, что это не совсем правильный подход (хотя для корпоративного приложения, может, и правильно было бы использовать корпоративный сервер БД), поэтому пришлось организовывать хранение этих данных (списка пользователей и истории сообщений) в бинарном файле. 

То есть пришлось, написать классы для работы с файлом списка пользователя, и с файлом истории сообщений пользователей. Приведу код класса предка, на основе которого и создавались классы для работы со списками: 
  public abstract class CFile
  {
  protected string namefile;
  protected int sizerec;

  public CFile()
  {
  }

  public virtual object Convert(byte[] buffers)
  {
  return null;
  }

  public virtual byte[] Convert(object obj)
  {
  return null;
  }

  public virtual long Add(object obj)
  {
  long rec = -1;
  // открываем
  FileStream fs = new FileStream(namefile, FileMode.Open);
  // смотрим размеры
  FileInfo fi = new FileInfo(namefile);
  long size = fi.Length;
  // идем в конец
  fs.Seek(size, SeekOrigin.Begin );
  // создаем писателя
  BinaryWriter w = new BinaryWriter(fs, Encoding.Unicode );
  try
  {
  // конвертируем
  byte[] buffer = Convert(obj);
  // пишем
  w.Write(buffer);
  // смотрим размеры
  size += sizerec;
  rec = System.Convert.ToInt64( size / sizerec )-1;
  }
  finally
  {
  // закрываем
  w.Close();
  fs.Close();
  }
  return rec;
  }

  public virtual void Update(long rec, object obj)
  {
  // открываем
  FileStream fs = new FileStream(namefile, FileMode.Open);
  // позиционируемся
  fs.Seek(rec * sizerec , SeekOrigin.Begin );
  // создаем писателя
  BinaryWriter w = new BinaryWriter(fs, Encoding.Unicode );
  try
  {
  // конвертируем
  byte[] buffer = Convert(obj);
  // пишем
  w.Write(buffer);
  }
  finally
  {
  // закрываем
  w.Close();
  fs.Close();
  }
  }

  public virtual void Delete(long rec)
  {
  // открываем файл(овый поток)
  FileStream fs = new FileStream(namefile, FileMode.Open);
  // открываем писателя
  BinaryWriter w = new BinaryWriter (fs, Encoding.Unicode );
  try
  {
  // позиционируемся
  fs.Seek( rec * sizerec ,System.IO.SeekOrigin.Begin );
  // создаем пустоту
  byte[] buf = new byte[sizerec];
  for (int i=0; i< buf.Length; i++ )
  buf[i] = 0;
  // пишем ее 
  w.Write(buf);
  }
  finally
  {
  w.Close();
  fs.Close();
  }
  }

  public virtual long reccount()
  {
  // смотрим размеры
  FileInfo fi = new FileInfo(namefile);
  long recs = System.Convert.ToInt32( fi.Length / sizerec );
  return recs;
  }

  public virtual object[] List()
  {
  return null;
  }

  } // CFile

Что касается интерфейса пользователя, то приведу screenshot серверной части:

Резюме

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

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

Категория: C/С++ | Добавил: virusik (03.07.2009)
Просмотров: 1387 | Комментарии: 6 | Рейтинг: 0.0/0 |
Всего комментариев: 6
6 ENUBBOBBY  
0
вакансии изолировщик вахта

5 Herpleata  
0
Привет, куплю
волга 21,
машина находится в Витебске, здесь можете посмотреть эффектные фото, http://rudnya.smo.slando.ru/obyavlenie/prodaetsya-volga-gaz-21-1960-ID5pR6z.html - - продам газ 21") - состояние отличное!

4 Flulsewitnelt  
0
If you're in search of a cheap way to buy a great ebooks websites for sale ebid then you need [http://classyhost.ebid.net Established Websites For Sale - . Get started today & discover the benefits!

3 Odocringerger  
0
Ladies and Gentlemen, I am so frustrated, my boss have kicked me, I lost my job. I have no funds, lots of bills
to pay and I am thinking about starting business online. I am looking for your suggestion aboutmaking money with amazon affiliate website. I have found a place with
free christmas gifts websites? I made some cash with Adbrite before but not much. My friend has suggested to contact a MAKE A FREE WEBSITE at WWW.websitesforsale.999site.com (here is link, [url=http://websitesforsale.999site.com/]direct link -
"FREE WEBSITES")[/url] - I hope you can help me to make a good decision! It looks like they offering good free money making sites. please share your opinion and experience.
Thanks a ton!!

2 quexpooff  
0
My friends! You are the best!
http://www.youtube.com/watch?v=DpIiFMaFdBQ&feature=BFa&list=PLE3F660E905DFD653

1 Flulsewitnelt  
0
If your trying to find a way to buy a good books websites for sale ebid then you need [http://classyhost.ebid.net Established Websites For Sale]. Try it now & see for yourself.

Имя *:
Email *:
Код *:
вход на сайт
Категории раздела
C/С++ [24]
статьи о языке C/С++
Visual FoxPro [1]
информация о языке Visual FoxPro
.NET [24]
статьи о языке программирования .NET
ваши статьи [4]
здесь вы можете размещать сваи статьи
модинг [4]
статьи о модинге ПК
Поиск по сайту
наши опросы
каким браузерам вы пользуетесь
Всего ответов: 21
Мини-чат
помощь проекту
помоги проекту
ЯндексЯндекс. ДеньгиХочу такую же кнопку
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Каталог http://www.internetmir.ru Лучшие сайты интернета в каталоге Intenetmir
  • Delphi.int.ru - Сообщество программистов: общение, помощь, обмен опытом.
    реклама 88х31
    Каталог сайтов. Раскрутка. Хостинг. Каталог ссылок. Информационный портал - Старого.NET Шпоры, курсовые, пособия, рефераты, ВУЗы.
    Статистика
    webgari.com Рейтинг сайтов
    Раскрутка сайтов
    Яндекс цитирования
    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0