Записка

advertisement
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
БРЯНСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ
УНИВЕРСИТЕТ
Кафедра «Информатика и программное обеспечение»
Разработка приложения «Чат»
КУРСОВОЙ ПРОЕКТ
по дисциплине «Сети ЭВМ и телекоммуникации»
Выполнил гр. 02-ПО2
Степуро С. А.
Преподаватель
Дроздов А. В.
БРЯНСК 2006
Задание
Разработать клиент – серверное приложение «Чат» для общения в
локальной сети в режиме реального времени.
Основанием для разработки служит задание на курсовой проект.
Описание работы
Для работы чата, должны быть запушены на удаленных машинах
серверная часть (для этого необходимо запустить Chat_Server.exe. рис.1) и
клиентская - Chat_Client.exe (максимум 50 клиентов(Max_Socket_Conect 50)).
Рис.1 Интерфейс серверной части
При нажатии кнопки Start Server – окно программы автоматически
сворачивается:
void CChat_ServerDlg::OnBnClickedButton1()
{
ShowWindow(SW_MINIMIZE); //Установить окно в свёрнутый вид
Now_Socket_Conect=-1;
AfxBeginThread(Start_Server, NULL, THREAD_PRIORITY_NORMAL); //Старт потока
}
и сервер начинает свою работу:
//Начало работы сервера. Инициализация сокетов, подключение клиентов.
UINT Start_Server (LPVOID pParam)
{
BOOL Run=TRUE;
BOOL NO_Error=TRUE;
char buf[5];
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); //Инициализация библиотеки
WS2_32.DLL для сокетов
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
SOCKET ServSocket;
sockaddr saClient;
int iClientSize = sizeof(saClient);
sockaddr_in saServer;
hostent* localHost;
char* localIP;
// создание слушающего сокета
ServSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ServSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
WSACleanup();
}
Сервер автоматически узнает IP адрес машины на которой он запущен
// Получение информации о Айпи-адресе
localHost = gethostbyname("");
localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);
// Set up the sockaddr structure
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = inet_addr(localIP);
saServer.sin_port = htons(1000);
// Связь сокета с портом
if (bind( ServSocket, (SOCKADDR*) &saServer, sizeof(saServer)) == SOCKET_ERROR) {
printf("bind() failed.\n"); closesocket(ServSocket);
}
И производит установку сокета в режим прослушивания на предмет
подключения:
if (listen( ServSocket, Max_Socket_Conect ) == SOCKET_ERROR)
AfxMessageBox("Error listening on socket.\n");
При запуске клиентской части загружается форма для ввода имени и
IP-адреса сервера: form.DoModal(); (рис.2).
Рис.2 форма для ввода имени и IP-адреса сервера
Осуществляется проверка на не пустое имя: if (strlen(form.Your_Name)==0) return FALSE;
и запуск функции инициализации данных и старта потока приёма сообщений
от сервера: Start_Get_Data();
Соединение с сервером происходит следующим образом:
if ( connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) ==
SOCKET_ERROR)
{
AfxMessageBox("FAILED Connected ");
WSACleanup();
return FALSE;
AfxMessageBox("Connect to server is Successful");
}
Connect=TRUE;
При успешном подключении ожидаем когда сервер передаст клиенту его
номер
iResult= recv( ConnectSocket, buf, sizeof(buf), 0);
if (iResult == SOCKET_ERROR)
{
AfxMessageBox("Server is lost. Reconnect to server.");
WSACleanup();
return FALSE;
}
ID=atoi(buf);
return TRUE;
}
Сервер же, при попытке подключения к нему нового клиента, выполняет
следующие действия:
while(Run)
{
если число подключенцев меньше чем максимальное
if (Now_Socket_Conect!=Max_Socket_Conect+1)
{
NO_Error=TRUE;
while( NO_Error )
{
увеличить число подключенцев
Now_Socket_Conect++;
//Ожидание соединения
AcceptSocket[Now_Socket_Conect] = accept(ServSocket, &saClient,
&iClientSize);
if (AcceptSocket[Now_Socket_Conect] == SOCKET_ERROR)
{
NO_Error=FALSE;;
break;
}
Передает клиенту его порядкового номера
itoa(Now_Socket_Conect,buf,10);//конвертация в строку
iResult=send( AcceptSocket[Now_Socket_Conect], buf, strlen(buf),
0/*MSG_DONTROUTE*/);
if (iResult == SOCKET_ERROR)
{
NO_Error=FALSE;
AfxMessageBox("FAILED send data");
WSACleanup();
}
else
И запускает поток для принятия сообщений от клиента
AfxBeginThread(Listen_Thread,
(LPVOID)AcceptSocket[Now_Socket_Conect], THREAD_PRIORITY_NORMAL);
NO_Error=FALSE;
}
}
}
WSACleanup();
return 0;
}
Приём сообщений от клиента
UINT Listen_Thread (LPVOID pParam) //содержит номер сокета клиента
{
int iResult;
char lpBuffer[SEND_BUFFER_SIZE];
char lpBuffer_ID[4];
char lpBuffer_Name[20];
пока сервер запущен
while ( Run )
{
получаем от клиента сообщения
Result=recv((SOCKET)pParam, lpBuffer, SEND_BUFFER_SIZE, 0);
if (iResult == SOCKET_ERROR)
{
//AfxMessageBox("FAILED receive data ID ");
Run=FALSE;
break; //если клиент отключился мы просто его забываем
}
//если получено число байт равное числу кодового сообщения
if (iResult == 24)
{
//то скопировать их в буфер
memcpy(lpBuffer_ID,lpBuffer,4);
memcpy(lpBuffer_Name,lpBuffer+4,20);
//и проверить их на "выход"
if (!memcmp( lpBuffer_Name, "Exit", 4 ))
Del_Client(atoi(lpBuffer_ID));//если да то удалить клиента
}
else//если клиент всё же прислал сообщение то разослать его всем
{
AfxBeginThread(Send_Message_to_All, (LPVOID)lpBuffer,
THREAD_PRIORITY_NORMAL);
}
}
return 0;
}
Отправка сообщений клиентом происходит следующим образом:
//Функция отправки сообщений на сервер
void CChat_ClientDlg::OnBnClickedButton1()
{
CString In;
CString Out;
char lpBuf[5];
char lpBuffer[SEND_BUFFER_SIZE];
int len = Type_String.LineLength(Type_String.LineIndex(0)); //получение размера сообщения
int iResult;
Если сообщение не пустое, то заполнить буфер для отправки на сервер
данными о номере клиента его имени и сообщением
if (len>0)
{
Type_String.GetLine(0, In.GetBuffer(len), len);//Записать его в буфер
In.Left(1000);//и обрезать до 1000 символов
itoa(ID,lpBuf,10);
//
memcpy(lpBuffer,lpBuf,4);
memcpy(lpBuffer+4,form.Your_Name,20);
memcpy(lpBuffer+24,In,1000);
Заполненный буфер отправляется на сервер:
iResult=send(ConnectSocket, lpBuffer, SEND_BUFFER_SIZE, 0);
В случае ошибки подключения выдается сообщение
if (iResult == SOCKET_ERROR)
{
AfxMessageBox("Server not found. Reconnect to server.");
WSACleanup();
}
Рис.3 Ошибка подключения
Сервер, получив сообщение от клиента, рассылает его всем остальным
клиентам:
//Отправка сообщений от одного клиента всем клиентам, через сервер.
UINT Send_Message_to_All (LPVOID pParam)
//содержит сообщение от клиента
{
int iResult;
char lpBuffer[SEND_BUFFER_SIZE];
memcpy(lpBuffer,pParam,SEND_BUFFER_SIZE); //Получаем в буфер сообщение от
клиента
for (int i=0;i<Now_Socket_Conect;i++) //Всем клиентам из массива сокетов
{
//отправляем сообщение
iResult=send(AcceptSocket[i],
lpBuffer,
SEND_BUFFER_SIZE,
0/*MSG_DONTROUTE*/);
if (iResult == SOCKET_ERROR)
{
AfxMessageBox("FAILED send data All");
return FALSE;
}
}
return 0;
}
Приём сообщений от сервера пока клиент запущен
while(!STOP)
{
//Ожидать и получать сообщения от сервера
iResult= recv(mS.socket, lpBuffer, SEND_BUFFER_SIZE, 0);
if (iResult == SOCKET_ERROR)
{
AfxMessageBox("Server is lost. Reconnect to server.");
return FALSE;
}
посылаем окну сообщение ::PostMessage(HWND(pParam),WM_USERMSG_DRAW,0,0);
Вывод сообщения в окно
LRESULT CChat_ClientDlg::Draw_Data(WPARAM wParam, LPARAM lParam)
{
char lpBuffer[SEND_BUFFER_SIZE];
char lpBuffer_Name[20];
char lpBuffer_Data[1000];
//Формируем 2 выводных буфера (Имя + сообщение)
memcpy(lpBuffer,mS.Data,SEND_BUFFER_SIZE);
memcpy(lpBuffer_Name,lpBuffer+4,20);
memcpy(lpBuffer_Data,lpBuffer+24,1000);
//Выводим в окно
Chat_Windows.ReplaceSel("FROM: ",0);
//От кого:
Chat_Windows.ReplaceSel(lpBuffer_Name,0); //Имя
Chat_Windows.ReplaceSel(" MESSAGE: ",0); //Сообщение:
Chat_Windows.ReplaceSel(lpBuffer_Data,0);//Текст сообщения
Chat_Windows.ReplaceSel("\r\n",0);//Перевод каретки
return 0;
}
При необходимости пользователь может очистить окно чата, нажав на
кнопку Clear.
Очистка окна:
void CChat_ClientDlg::OnBnClickedButton2()
{
Chat_Windows.SetWindowText("");
}
Закрытие клиента:
void CChat_ClientDlg::OnClose()
{
STOP=TRUE;
if(Connect)//Если клиент был подключён к серверу
{
char lpBuffer[24];
char lpBuffer_ID[4];
int iResult;
//Формируем специальное сообщение для сервера
itoa(ID,lpBuffer_ID,10);
memcpy(lpBuffer,lpBuffer_ID,4);
//Номер клиента
Формируем сообщение для сервера, о том что клиент закрывается:
memcpy(lpBuffer+4,"Exit",4);
И отправляем его:
iResult=send(ConnectSocket, lpBuffer, 24, 0);
if (iResult == SOCKET_ERROR)
{
//AfxMessageBox("Server is lost. Reboot programm.");
WSACleanup();
}
}
OnCancel();
}
Сервер, получив сообщение «на выход» клиента удаляет его
if (!memcmp( lpBuffer_Name, "Exit", 4 ))
Del_Client(atoi(lpBuffer_ID));
Если какой-либо из клиентов отключился, то остальные клиенты меняют
свой номер:
//Изменение номера клиента
LRESULT CChat_ClientDlg::Change_ID(WPARAM wParam, LPARAM lParam)//содержит
новый номер клиента
{
ID=(int)wParam;
mS.ID=(int)wParam;
return 0;
}
Отключение сервера
void CChat_ServerDlg::OnBnClickedButton2()
{
Run=FALSE;
OnCancel();
}
Список литературы
1. Компьютерные сети. Принципы, технологии, протоколы / В.Г. Олифер, Н.А.
Олифер. –
Спб; Издательство «Питер», 2000.
2. Фролов А. В., Фролов Г. В. Локальные сети персональных компьютеров.
Использование протоколов IPX, SPX, NetBIOS. – М.: Диалог-МИФИ, 1995. – 158 с.
3. Программирование в Microsoft Visual / И. И. Данилов –Спб; Издательство «Питер».
Download