ПО для рассылки и получения новостей

Автор работы: Пользователь скрыл имя, 17 Июня 2013 в 00:52, курсовая работа

Описание работы

Активно внедряя автоматизацию в сферу рассылки и получения новостей, руководство Министерства информации четко определило главную цель: повышение качества и уровня доставки информации, создание оптимальных условий для получения информации к пользователю. Успех в этой работе во многом зависит от того, насколько технологически грамотно весь этот сложный программно-технический комплекс будет использован для главной цели его создания – эффективной рассылки и получения новостей посредством технологии клиент – сервер на основе WINDOWS SOCKETS используя API функции.
Целью данной работы является составление программного обеспечения для рассылки и получения.

Содержание работы

Введение………………………………………….……………………………………41 Обзор состояния вопроса ель и задачи работы…………….……………………...5
2 Постановка задачи………………………………………………….………..……...7
3 Моделирование ПО……………………………….………………………………...8
3.1 Архитектура ПО…………………………….…………………………………..8
3.2 Интерфейс пользователя………………………………………………………11
3.3 Описание основных функциональных модулей……………………………..14
3.4 Структура классов и объектов………………………………………………...24
4 Реализация ПО……………………………………………………………………..28
5 Руководство пользователя………………………………...………………………29
Заключение…………………………...………………………………………………34Список используемой литературы…………………………………………...……..35

Файлы: 1 файл

курсовой_проект.doc

— 539.00 Кб (Скачать файл)

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

Существует вариация протокола NNTP, называемая NNRP (Network News Readers Protocol). Она  отличается только набором поддерживаемых команд, и предназначена для чтения конференций с сервера новостей клиентским ПО в режиме онлайн. Функционально NNTP ориентирован на то, что статьи отправляются подписчикам при их появлении на сервере, а NNRP — по запросу клиента. Соответственно, NNTP используется для обмена сообщениями между серверами новостей, а NNRP — для чтения сообщений с ньюссервера и создания новых сообщений.

За NNTP закреплён TCP-порт 119. При подключении  к NNTP-серверу по SSL (т. н. NNTPS) используется порт 563.

NNTP предполагает, что серверы новостей  имеют постоянное IP-подключение.  Когда онлайн-технологии были  менее распространены, серверы новостей  обменивались новостями посредством  uucp.

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

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

На рисунке 3.1.2 приведен пример NNTP-сессии, где А – отвечающая сторона (сервер), О – вызывающая (клиент).

 

Рисунок 3.1.2 – Пример NNTP – сессии

 

Иногда говорят «клиент групп  новостей» вместо «NNTP-клиент». Следует понимать, что NNTP-клиенты поддерживают протокол NNTP не в полной мере, а лишь его вариацию NNRP.

 

3.2 Интерфейс пользователя

 

Откроем MS Visual Studio. Net и создадим в нем интерфейс пользователя программы рассылки и получения новостей.

В первую очередь создадим серверную часть программы. Для этого создадим Windows Sockets API. Windows Sockets API или проще Winsock – это техническая спецификация, которая определяет, как сетевое программное обеспечение Windows будет получать доступ к сетевым сервисам, в том числе, TCP/IP. Он определяет стандартный интерфейс между клиентским приложением (таким как FTP клиент или веб-браузер) и внешним стеком протоколов TCP/IP. Он основывается на API модели сокетов Беркли, использующейся в BSD для установки соединения между программами.

Проще говоря, Winsock будет отвечать за транспортировку рассылки новостей пользователям. Создавая оболочку для Winsock воспользуемся консольным приложением (рисунок 3.2.1).

 

 

Рисунок 3.2.1 – консольный режим Windows Sockets API

 

Далее, следует создать интерфейс отправителя рассылки (рисунок 3.2.2).

 

 

Рисунок 3.2.2 – Форма подключения к серверу отправителя рассылки новостей

 

После того, как отправитель новостей подключится к серверу приема и отправки новостей (основе WINDOWS Socket), нужно будет реализовать окно для написания и отправки (рассылки) новостей (рисунок 3.2.3).

Рисунок 3.2.3 – Форма рассылки новостей

 

 

Итак, интерфейс отправителя рассылки создан. Теперь приступим к созданию интерфейса для пользователя, который  будет принимать данные рассылки.

Для этого создадим форму изображенную на рисунке 3.2.4.

Данная форма имеют общую  связь с формой авторизации отправителя  рассылки, но имеет разную смысловую  цель.

 

Рисунок 3.2.4 – Форма авторизации пользователя, принимающего рассылки новостей

Теперь остается создать основную часть формы, которая будет принимать  рассылки и отображать их в своем  окне (рисунок 3.2.5).

 

Рисунок 3.2.5 – Форма для получения рассылки новостей

 

На данном этапе все основные интерфейсы пользователей созданы, перейдем к описанию основных функциональных модулей.

 

3.3 Описание основных функциональных  модулей

 

Опишем по порядку основные функциональные модули.

Первый модуль – это модуль, который отвечает за соединение сокетов (Windows Sockets).

 

// клиентский сокет, использующий механизм пула

class xClient {

private:

SOCKET         client;

HANDLE         thread;

DWORD          tid;

volatile BOOL  loop;

public:

 

xClient(void) {

   tid    = 0u;

       client = 0u;

   thread = NULL;

   loop   = FALSE;

}

~xClient() {

       this->Close();

}

При приходе клиентского запроса  у сервера имеется несколько  вариантов действий:

- Обрабатывать все запросы в одном потоке;

- Обрабатывать каждый запрос в отдельном потоке;

- Организовать пул потоков.

Рассмотрим каждый из сценариев.

 

// при отключение потока подвесить  поток и задать ему самый  низкий приоритет

void  Detach(void) {

    EnterCriticalSection(&section);

        if(client != 0u) {

shutdown(client, SD_RECEIVE);

            closesocket(client);

}

client = 0u;

SetThreadPriority(thread, THREAD_PRIORITY_IDLE);

    LeaveCriticalSection(&section);

SuspendThread(thread);

}

 

// пробудить поток в жизнь

void Attach(SOCKET  sock) {

        client = sock;

SetThreadPriority(thread, THREAD_PRIORITY_NORMAL);

ResumeThread(thread);

}

 

BOOL IsConnected(void) const {

        return (client != 0u);

}

 

DWORD  GetID(void) const {

return tid;

}

 

private:

   

    static DWORD WINAPI  ThreadReader(LPVOID param) {

 

        xClient* obj = reinterpret_cast<xClient*>(param);

        int  result;

CHAR buf[BUFSIZE + 1];

 

while(obj->loop) {

 

if(! obj->IsConnected()) {

                Sleep(0u);

continue;

}

 

result = recv(obj->client, buf, BUFSIZE, 0);

if(result > 0) {

 

  EnterCriticalSection(&section);

              for(std::list<xClient*>::iterator iter = clients.begin(); iter != clients.end(); ++iter) {

 

  if(! (*iter)->IsConnected())

  continue;

 

  if((*iter)->GetID() != GetCurrentThreadId())

                     (*iter)->Send(buf, result);

 

  }

  LeaveCriticalSection(&section);

 

} else if(result == 0 || result == SOCKET_ERROR)

obj->Detach();

}

return 0u;

}

 

};

 

// Реализация  серверного - объекта TCP

class xTCPServer {

private:

     SOCKET  server;

     HANDLE  fiber;

public:

 

     xTCPServer(void) {

        server = 0u;

        fiber  = NULL;

     }

 

~xTCPServer() {

        this->Close();

 }

 

public:

 

// Функция по созданию серверного сокета, в параметре принимает номер порта используйте выше чем > 1024  IANA <= 1024

  // параметер count_lisen - задаёт  кол-во одновременных соедененний,  обычно указывают пять, зависит  от ресурсов сервера

  BOOL   Create(USHORT port, int count_lisen = 5) {

 

        sockaddr_in  addr;

        addr.sin_family       = AF_INET;

        addr.sin_port         = htons(port);

        addr.sin_addr.s_addr  = htonl(INADDR_ANY);

 

        server = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

        if(server == SOCKET_ERROR )

            return FALSE;

 

        int result = bind(server, (const sockaddr*)&addr, sizeof(addr));

        if(result == SOCKET_ERROR)

            return FALSE;

 

int size    = sizeof(int);

int bufsize = 0;

 

// проверяем размер приёмного-буфера tcp

if(getsockopt(server, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &size) != SOCKET_ERROR) {

if(bufsize < BUFSIZE) {

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

bufsize = BUFSIZE;

setsockopt(server, SOL_SOCKET, SO_RCVBUF, (const char*)&bufsize, sizeof(int));

}

}

 

// также проверяем посылаемого-буфера tcp

if(getsockopt(server, SOL_SOCKET, SO_SNDBUF, (char*)&bufsize, &size) != SOCKET_ERROR) {

if(bufsize < BUFSIZE) {

bufsize = BUFSIZE;

setsockopt(server, SOL_SOCKET, SO_SNDBUF, (const char*)&bufsize, sizeof(int));

}

}

 

        listen(server, count_lisen);

 

ZeroMemory(&section, sizeof(CRITICAL_SECTION));

InitializeCriticalSection(&section);

 

        fiber  = CreateThread(NULL, 0U, xTCPServer::ThreadAccept, (LPVOID)this, 0u, NULL);

        if(fiber == NULL )  {

             closesocket(server);

             server = NULL;

             return FALSE;

        }

        return TRUE;

  }

 

 

  void  Close(void) {

  DeleteCriticalSection(&section);

  if(fiber != NULL ) {

         TerminateThread(fiber, 0u);

         CloseHandle(fiber);

fiber = NULL;

  }

      if(server != 0u)

          closesocket(server);

      fiber  = NULL;

      server = 0u;

  }

 

 

  BOOL IsRun(void) const {

     return (server != 0u);

  }

 

private:

 

 

  static  DWORD WINAPI ThreadAccept(LPVOID  hParam)

 // поток для ожидания клиентских соединений

 

        xTCPServer*  svr = reinterpret_cast<xTCPServer*>(hParam);

        sockaddr_in  addr;

        int  size  = sizeof(sockaddr_in);

bool check = false;

 

        while(TRUE) {

 

ZeroMemory(&addr, sizeof(sockaddr_in));

            SOCKET  sock = accept(svr->server, (sockaddr*)&addr, &size );

 

            if(sock == INVALID_SOCKET)

                continue;

 

// попытаемся найти в пуле  найти совободный элемент

check = true;

for(std::list<xClient*>::iterator iter = clients.begin(); iter != clients.end(); ++iter) {

if(! (*iter)->IsConnected()) {  // если есть  свободный

(*iter)->Attach(sock);    // присоеденим нового клиента  к нему

check = false;

break;

}

}

 

// Если свободных элементов нет

if(check) {

 

EnterCriticalSection(&section);

 

xClient*  new_client = new xClient();

if(new_client->Create( sock ))

clients.push_back(new_client);

  // добавим  новый элемент в набор-пула

else {

delete new_client;

new_client = NULL;

}

 

LeaveCriticalSection(&section);

        }

        return 0u;

  }

};

 

int main(void) {

 

   // на одной машине только один экземпляр-сервера может быть запущен

   HANDLE event = CreateEventA(NULL, TRUE, FALSE, "Kudushteev");

   if(GetLastError() == ERROR_ALREADY_EXISTS) {

   CloseHandle(event);

   return 1;

   }

 

   WSADATA data = {0};

   if(WSAStartup(MAKEWORD(2, 2), &data) != 0)

   return 1;

 

   CHAR buf[32];

   HANDLE  hout = GetStdHandle(STD_OUTPUT_HANDLE);

   HANDLE hin  = GetStdHandle(STD_INPUT_HANDLE);

 

   if(hout == INVALID_HANDLE_VALUE || hin == INVALID_HANDLE_VALUE)

   return 1;

   SetConsoleTextAttribute(hout, FOREGROUND_RED | FOREGROUND_INTENSITY);

   SetConsoleTitleW(L"ПО для рассылки и получения новостей");

 

   CONSOLE_SCREEN_BUFFER_INFO sinfo = {0};

   GetConsoleScreenBufferInfo(hout, &sinfo);

   sinfo.dwSize.Y = 25;

   SetConsoleScreenBufferSize(hout, sinfo.dwSize);

 

   DWORD  dwrite = 0u;

   CharToOemA("Ведите порт, по которому будет осуществлятся рассылка новостей: ", buf);

   WriteConsoleA(hout, (LPVOID)buf, lstrlenA(buf), &dwrite, NULL);

 

   if(! ReadConsoleA(hin, (LPVOID)buf, sizeof(buf) - 1, &dwrite, NULL))

   return 1;

   FlushConsoleInputBuffer(hin);

  

   USHORT port = (USHORT)atoi(buf);

   port &= 0xFFFF;

 

   xTCPServer*  server = new xTCPServer();

   if(server->Create(port)) {

 

   CharToOemA("Для остановки сервера любая клавиша", buf);

       SetConsoleTextAttribute(hout, FOREGROUND_GREEN | FOREGROUND_INTENSITY);

   WriteConsoleA(hout, (LPVOID)buf, lstrlenA(buf), &dwrite, NULL);

       ReadConsoleA(hin,   (LPVOID)buf, sizeof(buf),   &dwrite, NULL);

 

   for(std::list<xClient*>::iterator iter = clients.begin(); iter != clients.end(); ++iter)

   delete (*iter);

   if(clients.size() > 0u)

   clients.clear();

   }

   delete server;

   server = NULL;

   WSACleanup();

   CloseHandle(event);

   return 0;

}

 

Второй модуль (output.cpp) – это модуль рассылки и приема новостей .

 

// Обработчик событий-сообщений  элемента управления(окно)

LRESULT  xEditOutput::ControlProc(UINT  msg, WPARAM  wParam, LPARAM  lParam) {

 

HDC         hdc;

RECT        rect;

SIZE        size;

HGDIOBJ     old;

PAINTSTRUCT paint;

SCROLLINFO  sinfo;

int i, off;

 

switch(msg) {

case WM_CREATE:

hdc  = GetDC(cwnd);

 

if((font = (HFONT) GetStockObject(DEFAULT_GUI_FONT)) == NULL)

            font = (HFONT) GetStockObject(SYSTEM_FONT);

 

// создём главный буфер контрола

        bdc = CreateCompatibleDC(hdc);

GetClientRect(cwnd, &rect);

bitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);

SelectObject(bdc, bitmap);

 

// заливка цвет-контрола

        FillRect(bdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));

 

SetBkMode(bdc, TRANSPARENT);

SelectObject(bdc, font);

if(! GetTextExtentPointA(bdc, "a", 1, &size))

GetTextExtentPoint32A(bdc, "a", 1, &size);

        font_cy = abs(size.cy);

 

// создаём остальные атрибуты текстовых блоков

brush_back  = CreateSolidBrush(RGB(200, 245, 255));

brush_back2 = CreateSolidBrush(RGB(230, 230, 230));

GetClientRect(cwnd, &rect);

 

        dc_border  = CreateCompatibleDC(hdc);

bmp_border = CreateCompatibleBitmap(hdc, size_txt.cx + 4, font_cy);

        SelectObject(dc_border, bmp_border);

 

        dc_border2  = CreateCompatibleDC(hdc);

bmp_border2 = CreateCompatibleBitmap(hdc, size_txt.cx + 4, font_cy);

        SelectObject(dc_border2, bmp_border2);

 

 

        old = SelectObject(dc_border, (HGDIOBJ)GetStockObject(DC_PEN));

for(i = 0; i < font_cy; i++) {

SetDCPenColor(dc_border, RGB(200 - i * 5, 255 - i * 5, 255 - i * 5));

MoveToEx(dc_border, 0, i, NULL);

LineTo(dc_border, size_txt.cx, i);

}

SelectObject(dc_border, old);

 

 

        old = SelectObject(dc_border2, (HGDIOBJ)GetStockObject(DC_PEN));

for(i = 0; i < font_cy; i++) {

SetDCPenColor(dc_border2, RGB(255 - min(i * 7, 255), 255 - min(i * 7, 255), 255 - min(i * 7, 255)));

MoveToEx(dc_border2, 0, i, NULL);

LineTo(dc_border2, size_txt.cx, i);

}

SelectObject(dc_border2, old);

 

       

ReleaseDC(cwnd, hdc);

break;

    case WM_PAINT:

hdc = BeginPaint(cwnd, &paint);

       

BitBlt(hdc, 0, paint.rcPaint.top, paint.rcPaint.right, paint.rcPaint.bottom, bdc, 0, 0, SRCCOPY);

 

EndPaint(cwnd, &paint);

break;

case WM_NCPAINT:

    InvalidateRect(cwnd, NULL, FALSE);

break;

case WM_MOUSEMOVE:

        SetFocus(cwnd);

break;

case WM_MOUSEWHEEL:

if(HIWORD(wParam) > 120)

             SendMessage(cwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);

    else

             SendMessage(cwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);

break;

case WM_VSCROLL:

        sinfo.cbSize = sizeof(SCROLLINFO);

        sinfo.fMask  = SIF_ALL;

        GetScrollInfo(cwnd, SB_VERT, &sinfo);

        off          = sinfo.nPos;

        sinfo.fMask  = SIF_POS;

 

switch(LOWORD(wParam)) {

case SB_THUMBTRACK:

                sinfo.nPos = sinfo.nTrackPos;

                break;

            case SB_LINEUP:

                sinfo.nPos--;

                break;

            case SB_LINEDOWN:

                sinfo.nPos++;

                break;

case SB_PAGEUP:

                sinfo.nPos -= 8;

break;

case SB_PAGEDOWN:

               sinfo.nPos  += 8;

break;

}

        SetScrollInfo(cwnd, SB_VERT, &sinfo, TRUE);

i = GetScrollPos(cwnd, SB_VERT);

if(off != i) {

    this->update(true);

ScrollWindow(cwnd, 0, off - i, NULL, NULL);

UpdateWindow(cwnd);

        InvalidateRect(cwnd, NULL, TRUE);

}

break;

case WM_DESTROY:

this->Destroy();

break;

}

return DefWindowProcA(cwnd, msg, wParam, lParam);

}

 

 

 

 

 

void  xEditOutput::Destroy(void) {

 

   DeleteCriticalSection(&section);

 

Информация о работе ПО для рассылки и получения новостей