Пишем чат для локальной сети, используя C++ Builder. Клиентская часть

от автора

Доброго времени суток.

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

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

Дизайн клиента очень простой, даже примитивный. Я не видел смысла создавать меню бар в приложении. На форме размещены 2 панели, одна из них меняет цвет, если клиент подключен к серверу она зеленая, иначе — красная. На следующей панели размещен TabControl. Я перепробовал 5 или 6 вариантов дизайна приложения, и самым удобным нашел использование компонента TabControl. В его вкладки заносятся имена пользователей находящихся в сети, при выборе соответствующей вкладки начинается переписка с этим пользователем(также выводится история сообщений). Сообщения выводятся в компонент Memo, писать сообщения надо в компонент Edit, отправка- по нажатию соответствующей кнопки или клавише Enter.

У клиента также реализовано сворачивание окна в область уведомлений.

void __fastcall TFormMain::DrawItem(TMessage& Msg) {      IconDrawItem((LPDRAWITEMSTRUCT)Msg.LParam);      TForm::Dispatch(&Msg); } //--------------------------------------------------------------------------- void __fastcall TFormMain::MyNotify(TMessage& Msg) {     POINT MousePos;      switch(Msg.LParam)     {         case WM_RBUTTONUP:             if (GetCursorPos(&MousePos))             {                 PopupMenu1->PopupComponent = FormMain;                 SetForegroundWindow(Handle);                 PopupMenu1->Popup(MousePos.x, MousePos.y);             }             else                 Show();             break;         case WM_LBUTTONDBLCLK:         Show();          break;         default:             break;     }     TForm::Dispatch(&Msg); } //---------------------------------------------------------------------------  //--------------------------------------------------------------------------- bool __fastcall TFormMain::TrayMessage(DWORD dwMessage) {    NOTIFYICONDATA tnd;    PSTR pszTip;     pszTip = TipText();     tnd.cbSize          = sizeof(NOTIFYICONDATA);    tnd.hWnd            = Handle;    tnd.uID             = IDC_MYICON;    tnd.uFlags          = NIF_MESSAGE | NIF_ICON | NIF_TIP;    tnd.uCallbackMessage	= MYWM_NOTIFY;     if (dwMessage == NIM_MODIFY)     {         tnd.hIcon		= (HICON)IconHandle();         if (pszTip)            lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip)); 	    else         tnd.szTip[0] = '\0';     }    else     {         tnd.hIcon = NULL;         tnd.szTip[0] = '\0';     }     return (Shell_NotifyIcon(dwMessage, &tnd)); } //--------------------------------------------------------------------------- HICON __fastcall TFormMain::IconHandle(void) { return (Image2->Picture->Icon->Handle); }  //--------------------------------------------------------------------------- PSTR __fastcall TFormMain::TipText(void) {         return ("Office Chat");  } //--------------------------------------------------------------------------- LRESULT IconDrawItem(LPDRAWITEMSTRUCT lpdi) { return 0; } //---------------------------------------------------------------------------  //---------------------------------------------------------------------------   void __fastcall TFormMain::FormDestroy(TObject *Sender) { 	TrayMessage(NIM_DELETE); } //---------------------------------------------------------------------------  void __fastcall TFormMain::N1Click(TObject *Sender) { Show(); } //---------------------------------------------------------------------------  void __fastcall TFormMain::N2Click(TObject *Sender) { Application->Terminate(); } //---------------------------------------------------------------------------  void __fastcall TFormMain::FormCloseQuery(TObject *Sender, bool &CanClose) { CanClose=false; FormMain->Hide(); } //--------------------------------------------------------------------------- 

При поступлении нового сообщения воспроизводится приятный звук.

За всю работу с сетью отвечает стандартный компонент ClientSocket. Клиент, также как и сервер, принимая сообщения первым делом отделяет от них первые 4 символа. Затем определяется что делать дальше. Все происходит в событии OnRead.
Код 4796 значит что клиенту следует отправить свое имя для «регистрации» на сервере.
7788 — один из самых главных кодов, он ставится в начале входящего сообщения. При этом отделяется имя отправителя и само сообщения. Далее идет проверка на состояние окна клиента. Если открыта вкладка с диалогом отправителя сообщения то сообщение просто добавляется в новую строку Memo, в начале добавляется время поступления сообщения. Если открыта вкладка переписки с другим пользователем, воспроизводится звук и во вкладке с нужным пользователем после имени добавляется надпись "+1". При переходе в эту вкладку надпись убирается. Если окно приложения свернуто то выплывает контекстное меню с двумя вариантами: закрыть меню и перейти к переписке. Меню вызывается на всех компьютерах в одинаковом месте — правом верхнем углу. Для этого определяется разрешение монитора. В любом из случаев сообщение вместе со временем поступления вносится в файл. Для каждого пользователя есть свой файл переписки, из него же берется история переписки.
8714 — значит, что пора обновить список пользователей в TabControl.

void __fastcall TForm1::ClientSocketRead(TObject *Sender,       TCustomWinSocket *Socket) { AnsiString str=Now().CurrentDateTime(); message=Socket->ReceiveText(); AnsiString recieveText=message; AnsiString Text=recieveText; if(message.SubString(1,4).AnsiCompare("4796")==0) { ClientSocket->Socket->SendText("6141"+myname); } else if(message.SubString(1,4).AnsiCompare("7788")==0) {         if(TabControl1->Tabs->operator [](TabControl1->TabIndex).AnsiCompare(message.SubString(5,message.Pos(":")-5))==0)         {         ListBox->Lines->Add(str+"  "+message.SubString(5,message.Length()));         file(message.SubString(5,message.Pos(":")-5),message.SubString(message.Pos(":")+1,message.Length()),Now().CurrentDateTime(),"in");                 PlaySound("message.wav",0,SND_ASYNC);         PopupMenu2->PopupComponent = Form1;         PopupMenu2->Popup(GetSystemMetrics(SM_CXSCREEN)-200, 20);         }         else if(TabControl1->Tabs->operator [](TabControl1->TabIndex).AnsiCompare(message.SubString(5,message.Pos(":")-5))!=0)         {         PlaySound("message.wav",0,SND_ASYNC);         AnsiString im;         AnsiString mess;         im=message.SubString(5,message.Pos(":")-5);         mess=message.SubString(message.Pos(":")+1,message.Length());         file(im,mess,Now().CurrentDateTime(),"in");         AnsiString name=message.SubString(5,message.Pos(":")-5);         active=TabControl1->TabIndex;         count=TabControl1->Tabs->Count;         AnsiString n;         TabControl1->Tabs->Clear();         for(int i=0;i<count;i++)         {         n=m[i];         if(n.AnsiCompare(name)!=0)         TabControl1->Tabs->Add(n);         else if(n.AnsiCompare(name)==0)         TabControl1->Tabs->Add(n+"+1");          }         } } else if(message.SubString(1,4).AnsiCompare("8714")==0) { TabControl1->Tabs->Clear(); AnsiString  str=message.SubString(5,message.Length());  const char separator[]=","; int i=0;     char *Ptr=NULL;      Ptr=strtok(str.c_str(),separator);     while (Ptr)     {     //if(myname.AnsiCompare(Ptr)!=0)     TabControl1->Tabs->Add(Ptr);     strcpy(m[i],Ptr);         Ptr=strtok(0,separator);        i++;     }  } } 

В одной папке с приложением обязательно должен храниться файл конфигурации( с чисто символическим расширением ".config", потом сделаю нормальный INI-файл). В файле три строчки: имя клиента, ip-адрес сервера и версия приложения. При запуске приложения все это извлекается из файлы и используется по назначению.

Итог:
Мне удалось разработать примитивную, но все таки очень работоспособную программу позволяющую поболтать на рабочем месте связать несколько десятков компьютеров в офисе и прекратить общение с помощью телефонных звонков или тем более хождения по офису.
У меня появились кое-какие идеи, которые в будущем будут реализованы, например добавление графического чата, возможности пересылки файлов, может быть голосовой чат. Все это, конечно же надо рфализовыватьне помощью билетера 2006 года. Проект, например я уже перенес в Embarcadero RAD Studio XE8, очень уж нужна была версия на мак.

На этом думаю все, самое важное я написал. Очень надеюсь что данная статья будет полезна для начинающих работать в C++ Builder. Все исходники и сама программа тут

P.S. Первую часть статьи заминусовали, но первый опыт написания статей мне очень понравился. Комментируйте, буду исправлять свои ошибки. Спасибо за уделенное внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/259655/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *