Доброго времени суток.
Это продолжение статьи, в котором я расскажу о создании клиента для моего чата.
Клиентской части мною было уделено гораздо больше времени серверной, так как здесь есть вторая важная составляющая — графическая часть.
Дизайн клиента очень простой, даже примитивный. Я не видел смысла создавать меню бар в приложении. На форме размещены 2 панели, одна из них меняет цвет, если клиент подключен к серверу она зеленая, иначе — красная. На следующей панели размещен TabControl. Я перепробовал 5 или 6 вариантов дизайна приложения, и самым удобным нашел использование компонента TabControl. В его вкладки заносятся имена пользователей находящихся в сети, при выборе соответствующей вкладки начинается переписка с этим пользователем(также выводится история сообщений). Сообщения выводятся в компонент Memo, писать сообщения надо в компонент Edit, отправка- по нажатию соответствующей кнопки или клавише Enter.
У клиента также реализовано сворачивание окна в область уведомлений.
При поступлении нового сообщения воспроизводится приятный звук.
За всю работу с сетью отвечает стандартный компонент ClientSocket. Клиент, также как и сервер, принимая сообщения первым делом отделяет от них первые 4 символа. Затем определяется что делать дальше. Все происходит в событии OnRead.
Код 4796 значит что клиенту следует отправить свое имя для «регистрации» на сервере.
7788 — один из самых главных кодов, он ставится в начале входящего сообщения. При этом отделяется имя отправителя и само сообщения. Далее идет проверка на состояние окна клиента. Если открыта вкладка с диалогом отправителя сообщения то сообщение просто добавляется в новую строку Memo, в начале добавляется время поступления сообщения. Если открыта вкладка переписки с другим пользователем, воспроизводится звук и во вкладке с нужным пользователем после имени добавляется надпись "+1". При переходе в эту вкладку надпись убирается. Если окно приложения свернуто то выплывает контекстное меню с двумя вариантами: закрыть меню и перейти к переписке. Меню вызывается на всех компьютерах в одинаковом месте — правом верхнем углу. Для этого определяется разрешение монитора. В любом из случаев сообщение вместе со временем поступления вносится в файл. Для каждого пользователя есть свой файл переписки, из него же берется история переписки.
8714 — значит, что пора обновить список пользователей в TabControl.
В одной папке с приложением обязательно должен храниться файл конфигурации( с чисто символическим расширением ".config", потом сделаю нормальный INI-файл). В файле три строчки: имя клиента, ip-адрес сервера и версия приложения. При запуске приложения все это извлекается из файлы и используется по назначению.
Мне удалось разработать примитивную, но все таки очень работоспособную программу позволяющуюпоболтать на рабочем месте связать несколько десятков компьютеров в офисе и прекратить общение с помощью телефонных звонков или тем более хождения по офису.
У меня появились кое-какие идеи, которые в будущем будут реализованы, например добавление графического чата, возможности пересылки файлов, может быть голосовой чат. Все это, конечно же надо реализовывать не с помощью билдера 2006 года. Проект, например я уже перенес в Embarcadero RAD Studio XE8, очень уж нужна была версия на мак.
На этом думаю все, самое важное я написал. Очень надеюсь что данная статья будет полезна для начинающих работать в C++ Builder. Все исходники и сама программа тут.
P.S. Первую часть статьи заминусовали, но первый опыт написания статей мне очень понравился. Комментируйте, буду исправлять свои ошибки. Спасибо за уделенное внимание!
Это продолжение статьи, в котором я расскажу о создании клиента для моего чата.
Клиентской части мною было уделено гораздо больше времени серверной, так как здесь есть вторая важная составляющая — графическая часть.
Дизайн клиента очень простой, даже примитивный. Я не видел смысла создавать меню бар в приложении. На форме размещены 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. Первую часть статьи заминусовали, но первый опыт написания статей мне очень понравился. Комментируйте, буду исправлять свои ошибки. Спасибо за уделенное внимание!
Комментарии (9)
farcaller
06.06.2015 15:32Как насчет хотя бы через uncrustify прогнать исходники? Форматирование вообще никакое же. Код лучше оно, естественно, не сделает.
Dimezis
Код ужасен, практическая польза статьи тоже не особо понятна.
altanium Автор
Меня уже поругали, буду исправлять. Практическая польза я считаю есть, лично мне такая статья помогла бы когда я программу разрабатывал.
AdmAlexus
Понимаете. Если бы вы писали узкоспециализированную программу, для которой нет конкурентов (ну я не знаю, например «Расчет оптимальной толщины куска мяса, для качественной прожарки в условиях Крайнего Севера»), то вас оценили бы. А в случае чата для локалки… Ведь есть множество платных и бесплатных аналогов с большим функционалом. Да и студенты каждый год пишут я думаю множество таких программулек (сам в свое время писал, используя для отправки NET SEND).
Поэтому, учитывая ваш комментарий ниже, «Пишите про Ардуино».
Всех благ и удачи.
altanium Автор
Согласен с вами, особенно с тем что в интернете найдется 1000 похожих программ которые в 1000 раз лучше моей. Но если бы я залил статью о том как я ищу в интернете чат для локальной сети, написаный на билдере, это восприняли бы еще хуже =) И это и был проект для студенческой олимпиады, который достаточно высоко оценили( наверное у остальных просто все было даже еще хуже чем у меня).
Mixim333
Простите, но у нас эта тема была даже не «для студенческой олимпиады», а для курсовой по Сетям ЭВМ на 3 курсе (у самого была тема «Передача видеопотока по сети»)!
r0b1n
Простите, но помогла бы научиться плохому. Ну или на примере вашего когда можно рассказывать как делать не надо. Лучше попробуйте вместо кодинга продумать структуру программы, поискать какие патерны/подходы можно использовать в различных частях приложения и на какие компоненты его разбить.