Автор работы: Пользователь скрыл имя, 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
NNTP по большей части разработан для обмена сообщениями в телеконференциях. По строению этот протокол во многом сходен с протоколом приема и передачи электронной почты SMTP.
Существует вариация протокола NNTP, называемая NNRP (Network News Readers Protocol). Она отличается только набором поддерживаемых команд, и предназначена для чтения конференций с сервера новостей клиентским ПО в режиме онлайн. Функционально NNTP ориентирован на то, что статьи отправляются подписчикам при их появлении на сервере, а NNRP — по запросу клиента. Соответственно, NNTP используется для обмена сообщениями между серверами новостей, а NNRP — для чтения сообщений с ньюссервера и создания новых сообщений.
За NNTP закреплён TCP-порт 119. При подключении к NNTP-серверу по SSL (т. н. NNTPS) используется порт 563.
NNTP предполагает, что серверы новостей
имеют постоянное IP-подключение.
Когда онлайн-технологии были
менее распространены, серверы новостей
обменивались новостями
Протокол 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(§ion)
if(client != 0u) {
shutdown(client, SD_RECEIVE);
closesocket(client);
}
client = 0u;
SetThreadPriority(thread, THREAD_PRIORITY_IDLE);
LeaveCriticalSection(§ion)
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*>(
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(§ion)
for(std::list<xClient*>::
if(! (*iter)->IsConnected())
continue;
if((*iter)->GetID() != GetCurrentThreadId())
(*iter)->Send(buf, result);
}
LeaveCriticalSection(§ion)
} 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(§ion, sizeof(CRITICAL_SECTION));
InitializeCriticalSection(&
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(&
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*>(
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*>::itera
if(! (*iter)->IsConnected()) { // если есть свободный
(*iter)->Attach(sock); // присоеденим нового клиента к нему
check = false;
break;
}
}
// Если свободных элементов нет
if(check) {
EnterCriticalSection(§ion)
xClient* new_client = new xClient();
if(new_client->Create( sock ))
clients.push_back(new_client);
// добавим новый элемент в набор-пула
else {
delete new_client;
new_client = NULL;
}
LeaveCriticalSection(§ion)
}
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 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(
sinfo.dwSize.Y = 25;
SetConsoleScreenBufferSize(
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*>::
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 = (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_
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_
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_
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(&