Разработка программы для обмена тестовыми сообщениями

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

    Содержание: 

    1. Введение……………………………………………………………3 

    2. Инструментальное обеспечение для обмена текстовыми

        сообщениями……………………………………………….……...4 

    3. Разработка архитектуры программы для обмена текстовыми

        сообщениями…………………………...........................................5 

    4. Программная реализация сетевой технологии обмена

        текстовыми сообщениями в программе ………………………...8 

    5. Результаты работы программы…………………………………...25 

    6. Преимущества и недостатки приложения……………………….27 

    7. Заключение………………………………………………………...28 

    8. Список литературы………………………………………………..29 
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

 

1.Введение

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

    В настоящее время значительная часть программных продуктов написана на языке C++. Увеличение количества персональных ЭВМ в 80-90 гг. и вовлечение большого количества неквалифицированных пользователей в непосредственную работу на ЭВМ способствовало развитию операционных систем типа Windows. Эти операционные системы предполагают организацию процедур ввода-вывода не так как системы DOS.

    Специализированный  язык CBuilder предназначен для разработки интерфейсов (процедур ввода-вывода с экрана монитора, клавиатуры и др. внешних устройств).Эти интерфейсы могут работать как с приложениями, написанными на языке C++, так и на других языках.

    Язык  CBuilder представляет собой среду программирования, включающую:

    редактор, компилятор, загрузчик – программные  средства, позволяющие программисту использовать все средства языка;

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

    комплект  документации, и инструкций (help).

    Ключевой  особенностью программирования под  CBuilder является использование технологии Drag-and-Drop, позволяющей программисту формировать код будущего интерфейса, двигая по экрану монитора указанные “мышью” стандартные объекты.

    CBuilder существенно облегчает разработку программных продуктов систем управления базами данных.

    Таким образом, CBuilder существенно снижает трудозатраты программиста на написание интерфейса и систем управления базами данных.

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

 

    2.Инструментальное обеспечение разработки программы для обмена текстовыми сообщениями

    TClientSocket manages socket connections for a TCP/IP client.

    Unit Scktcomp

        Syntax

        [C++] class TClientSocket : public Scktcomp::TCustomSocket

    Description Add a TClientSocket object to a form or data module to turn an application into a TCP/IP client. TClientSocket specifies a desired connection to a TCP/IP server, manages the connection when it is open, and terminates the connection when the application is through. 

    TServerSocket manages server socket connections for a TCP/IP server.

    Unit Scktcomp

        Syntax

        [C++] class TServerSocket : public Scktcomp::TCustomServerSocket

    Description Add a TServerSocket object to a form or data module to turn an application into a TCP/IP server. TServerSocket listens for requests for TCP/IP connections from other machines, and establishes connections when requests are received.

    TTrayIcon Class  Creates an icon in the system tray next to the clock.

    Description Use the TTrayIcon component to create an icon in the system tray next to the clock. TTrayIcon has events for mouse clicks, properties for menus and animation of the icon, and methods for showing balloon hints.  
3.Разработка архитектуры программы для обмена текстовыми сообщениями 

      Задачей нашей программы является  обмен текстовыми сообщениями  через локальную сеть. Для работы  с сетью я выбрал компоненты  TServerSocket и TClientSocket. Это клиент-серверные компоненты – один компонент ожидает соединения, а другой с ним соединяется. К серверному компоненту могут подключаться сразу несколько клиентских, что позволяет нам создать многопользовательскую программы для обмена текстовыми сообщениями.

    Программа включает в себя клиентскую и серверную часть.

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

    Возможно, кому-то покажется интересной схема отсылки сообщения всем участникам «чата». Если кто то из «клиентов» отправляет сообщение, оно , естественно, доставляется серверу. Сервер же, получив сообщение от одного из клиентов, отправляет ее всем участникам «чата» , которые в свою очередь увидят входящее сообщение в поле Memo окна «чата».  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Рис. 1 
 
 
 
 
 
 
 
 
 
 
 
 

 
4.Программная реализация сетевой технологии обмена текстовыми сообщениями в программе 

Исходный  код главной формы

#include "stdafx.h"

#include "ChatCppDlg.h"

#include ".\chatcppdlg.h" 

#ifdef _DEBUG

#define new DEBUG_NEW

#endif 

class CAboutDlg : public CDialog

{

public:

      CAboutDlg(); 

// Dialog Data

      enum { IDD = IDD_ABOUTBOX }; 

      protected:

      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support 

// Implementation

protected:

      DECLARE_MESSAGE_MAP()

}; 

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)

{

} 

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

} 

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)

END_MESSAGE_MAP() 
 

// CChatCppDlg dialog 
 
 

CChatCppDlg::CChatCppDlg(CWnd* pParent /*=NULL*/)

      : CDialog(CChatCppDlg::IDD, pParent)

{

      m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

} 

void CChatCppDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      DDX_Control(pDX, IDC_RADIO_SERVER, m_ButtonStartServer);

      DDX_Control(pDX, IDC_RADIO_CLIENT, m_ButtonStartClient);

      DDX_Control(pDX, IDC_EDIT_NAME, m_wndName);

      DDX_Control(pDX, IDC_EDIT_IP, m_wndIPAddress);

      DDX_Control(pDX, IDC_EDIT_PORT, m_wndPort);

      DDX_Control(pDX, IDC_EDIT_SEND, m_wndSend);

      DDX_Control(pDX, IDC_BUTTON_SEND, m_ButtonSend);

      DDX_Control(pDX, IDC_BUTTON_STOPCHAT, m_ButtonStopChat);

      DDX_Control(pDX, IDC_EDIT_CHAT, m_wndChat);

      DDX_Control(pDX, IDC_STATIC_COUNTPEOPLE, m_wndCountPeople);

} 

BEGIN_MESSAGE_MAP(CChatCppDlg, CDialog)

      ON_WM_SYSCOMMAND()

      ON_WM_PAINT()

      ON_WM_QUERYDRAGICON()

      //}}AFX_MSG_MAP

      ON_BN_CLICKED(IDC_RADIO_SERVER, OnBnClickedRadioServer)

      ON_BN_CLICKED(IDC_RADIO_CLIENT, OnBnClickedRadioClient)

      ON_BN_CLICKED(IDC_BUTTON_STOPCHAT, OnBnClickedButtonStopchat)

      ON_BN_CLICKED(IDC_BUTTON_SEND, OnBnClickedButtonSend)

      ON_WM_CLOSE()

END_MESSAGE_MAP() 
 

// CChatCppDlg message handlers 

BOOL CChatCppDlg::OnInitDialog()

{

      CDialog::OnInitDialog(); 

      // Add "About..." menu item to system menu. 

      // IDM_ABOUTBOX must be in the system command range.

      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

      ASSERT(IDM_ABOUTBOX < 0xF000); 

      CMenu* pSysMenu = GetSystemMenu(FALSE);

      if (pSysMenu != NULL)

      {

            CString strAboutMenu;

            strAboutMenu.LoadString(IDS_ABOUTBOX);

            if (!strAboutMenu.IsEmpty())

            {

                  pSysMenu->AppendMenu(MF_SEPARATOR);

                  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

            }

      } 

      // Set the icon for this dialog.  The framework does this automatically

      //  when the application's main window is not a dialog

      SetIcon(m_hIcon, TRUE);   // Set big icon

      SetIcon(m_hIcon, FALSE);  // Set small icon 
 

      //////////////////////////////////////////////////////////////////////////////

      // Необходимая инициализация

      SetWindowText("Нет сети!"); 

      m_wndName.SetLimitText(12); // Ограничение количества вводимых символов.

      m_wndName.SetWindowText(g_EmptyName); 

      m_wndSend.SetLimitText(200); // Ограничение количества вводимых символов. 

      m_wndIPAddress.SetWindowText("127.0.0.1"); // Адрес по умолчанию для тестирования.

      m_wndPort.SetWindowText("500"); // Порт по умолчанию. 

      m_ButtonStopChat.SetWindowText(g_strExitFromChat);

      m_mainSocket.m_pParent = this; 
 

      return TRUE;  // return TRUE  unless you set the focus to a control

} 

void CChatCppDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

      if ((nID & 0xFFF0) == IDM_ABOUTBOX)

      {

            CAboutDlg dlgAbout;

            dlgAbout.DoModal();

      }

      else

      {

            CDialog::OnSysCommand(nID, lParam);

      }

} 

// If you add a minimize button to your dialog, you will need the code below

//  to draw the icon.  For MFC applications using the document/view model,

//  this is automatically done for you by the framework. 

void CChatCppDlg::OnPaint()

{

      if (IsIconic())

      {

            CPaintDC dc(this); // device context for painting 

            SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); 

            // Center icon in client rectangle

            int cxIcon = GetSystemMetrics(SM_CXICON);

            int cyIcon = GetSystemMetrics(SM_CYICON);

            CRect rect;

            GetClientRect(&rect);

            int x = (rect.Width() - cxIcon + 1) / 2;

            int y = (rect.Height() - cyIcon + 1) / 2; 

            // Draw the icon

            dc.DrawIcon(x, y, m_hIcon);

      }

      else

      {

            CDialog::OnPaint();

      }

} 
 

// The Системные вызовы эта функция, чтобы получить курсор, чтобы показать, в то время    как пользователь тянется

//  минимизировать окно.

HCURSOR CChatCppDlg::OnQueryDragIcon()

{

      return static_cast<HCURSOR>(m_hIcon);

} 

////////////////////////////////////////////// БЛОК 1////////////////////////////////////////////////////////////

// Запускаем сервер

void CChatCppDlg::OnBnClickedRadioServer()

{ 

      // Контроль несанкционированного  запуска сервера:

      // Если кнопка не  в состоянии нажатой,

      // если сокет в  работе (т.е. только с нулевым сокетом можно начинать                                                                                      работать),

      // очень неудобно  если в чате  все будут под  одинаковыми именами.

      if(m_ButtonStartServer.GetCheck() != BST_CHECKED) return; 

      if(m_mainSocket.m_hSocket != INVALID_SOCKET) return;

      if(QueryName() == false)

      {

            AfxMessageBox("ВВедите свое имя для чата!");

            StopChat();

            return;

      } 

      CString strPort;

      m_wndPort.GetWindowText(strPort);

      // Класс CAsyncSocket упрощает процедуру создания сокета,

      // вкладывая в функцию  Create() непосредственное создание

      // сокета и связывание  его с одним  из IP адресом доступном на компьютере.

      // atoi конвертирует  строку ansi to int

      if(m_mainSocket.Create(atoi(strPort)) == FALSE)

      {

            AfxMessageBox("Ошибка создания сервера!\nВозможно данный порт уже используется!");

            StopChat();

      }

      else

      {

            // Вводим сокет в  режим прослушивания  запросов на подключения.

            if(m_mainSocket.Listen() == FALSE)

            {

                  StopChat();

            }

            else

            {

                  // Если все в порядке.

                  DisabledControl(true);

                  SetWindowText("Сервер  ждет подключения!");

                  // В чате только  сервер.

                  m_wndCountPeople.SetWindowText("В чате 1 чел.");

            }

      }

}

/////////////////////////////////////////////////////////// БЛОК 1//////////////////////////////////////////////////////////////// 
 
 

/////////////////////////////////////////////////////////// БЛОК 2////////////////////////////////////////////////////////////////

// Запускаем клиента

void CChatCppDlg::OnBnClickedRadioClient()

{ 

      // Контроль несанкционированного запуска клиента:

      // Если кнопка не  в состоянии нажатой,

      // если сокет в  работе (т.е. только  с нулевым сокетом  можно начинать  работать),

      // очень неудобно  если в чате  все будут под  одинаковыми именами..

      if(m_ButtonStartClient.GetCheck() != BST_CHECKED) return;

      if(m_mainSocket.m_hSocket != INVALID_SOCKET) return;

      if(QueryName() == false)

      {

            AfxMessageBox("ВВедите свое имя для чата!");

            StopChat();

            return;

      } 
 

      // Класс CAsyncSocket упрощает процедуру создания сокета,

      // вкладывая в функцию  Create() непосредственное создание

      // сокета и связывание  его с одним  из IP адресом доступном на компьютере.

      if(m_mainSocket.Create() == TRUE)

      {

            CString strAddress;

            m_wndIPAddress.GetWindowText(strAddress);

            CString strPort;

            m_wndPort.GetWindowText(strPort);

            if(m_mainSocket.Connect(strAddress, atoi(strPort)) == FALSE)

            {

                  // В ассинхронном  режиме код этой  ошибки

                  // считается как  ожидание события  подключения,

                  // т.е. практически  успешный возврат.

                  if(GetLastError() == WSAEWOULDBLOCK)

                  {

                        DisabledControl(false);

                  }

                  else

                  {

                        // Если какая-либо  ошибка возникла,

                        // приводим приложение  в первоначальное  состояние,

                        // готовым к следующей  попытке создания  соединения.

                        StopChat();

                  }

            }

      } 

}

/////////////////////////////////////////////////////////// БЛОК 2//////////////////////////////////////////////////////////////// 
 

/////////////////////////////////////////////////////////БЛОК 1.1//////////////////////////////////////////////////////////////// 

// Нажали кнопку "Выйти из чата".

void CChatCppDlg::OnBnClickedButtonStopchat()

{

      StopChat();

} 

// Запрещает доступ  к управлениям  при работе 

// приложения в режиме  сервера или клиента.

// Цель запрета - избежать исключения  от 

// случайного нажатия "неправильных" кнопок.

void CChatCppDlg::DisabledControl(bool server)

{

      // Запреты.

      m_wndIPAddress.EnableWindow(FALSE);

      m_wndPort.EnableWindow(FALSE);

      m_ButtonSend.EnableWindow(FALSE); 

      if(server == true)

      {

            m_ButtonStopChat.SetWindowText(g_strStopChat);

            m_ButtonStartClient.EnableWindow(FALSE);

      }

      else

      {

            m_ButtonStopChat.SetWindowText(g_strExitFromChat);

            m_ButtonStartServer.EnableWindow(FALSE);

      } 

      // Разрешения.

      // Разрешить возможность  выхода из чата.

      m_ButtonStopChat.EnableWindow(TRUE);

} 

// Разрешить доступ  к управлениям  после закрытия  сокетов.

// Цель запрета - избежать исключения  от

// случайного нажатия  "неправильных" кнопок.

void CChatCppDlg::EnabledControl(void)

{

      // Разрешения.

      m_wndIPAddress.EnableWindow(TRUE);

      m_wndPort.EnableWindow(TRUE);

      m_ButtonStartClient.EnableWindow(TRUE);

      m_ButtonStartServer.EnableWindow(TRUE); 
 

      // Запреты.

      m_ButtonStopChat.EnableWindow(FALSE);

      m_ButtonSend.EnableWindow(FALSE);

}

/////////////////////////////////////////////////////////БЛОК 1.1//////////////////////////////////////////////////////////////// 

/////////////////////////////////////////////////////////БЛОК 1.2////////////////////////////////////////////////////////////////

// Принимаем запросы  на подключения

void CChatCppDlg::OnAccept(void)

{

      CSock* pSock = new CSock;

      pSock->m_pParent = this;

      // Если все в порядке  добавим рабочий  сокет в список 

      // подключенных рабочих  сокетов.

      if(m_mainSocket.Accept(*pSock) == TRUE)

      {

            m_vecSockets.push_back(pSock);

            m_ButtonSend.EnableWindow(TRUE);

            SendCountPeople();

      }

      else

            delete pSock; 
 

      SetWindowText("Сеть  работает!");

}

/////////////////////////////////////////////////////////БЛОК 1.2//////////////////////////////////////////////////////////////// 
 

/////////////////////////////////////////////////////////БЛОК 1.3////////////////////////////////////////////////////////////////

// Выход из чата,

// если это сработало  на стороне сервера,

// то это полная  остановка чата.

// Для более надежной  работы чата, во  всех

// приложениях должны быть запущеня дублирующие серверы...

void CChatCppDlg::StopChat(void)

{

      // Отсылаем сигнал  об отключении  от чата.

      SendDisconnect(); 

      m_mainSocket.Close();

      for(int i = 0; i < (int)m_vecSockets.size(); i++)

      {

            m_vecSockets[i]->Close();

            delete m_vecSockets[i];

      }

      // Очистим вектор  от ненужных элементов.

      m_vecSockets.clear(); 

      m_ButtonStartServer.SetCheck(BST_UNCHECKED);

      m_ButtonStartClient.SetCheck(BST_UNCHECKED); 

      // Разрешим доступ  к управлению для

      // повторных попыток.

      EnabledControl(); 

      // В чате нет никого.

      m_wndCountPeople.SetWindowText("В  чате 0 чел.");

      SetWindowText("Нет сети!");

}

/////////////////////////////////////////////////////////БЛОК 1.3//////////////////////////////////////////////////////////////// 
 
 

Разработка программы для обмена тестовыми сообщениями