Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Qt. Лабораторная работа. Сетевое программирован....docx
Скачиваний:
18
Добавлен:
22.11.2018
Размер:
154.22 Кб
Скачать

Содержание

1. Сетевое программирование в qt 1

1.1. Обзор классов и примеры реализаций 1

1.2. Клиент-серверная архитектура 1

1.3. Реализация сервера 1

1.4. Реализация клиента 5

1.5. Заключение 8

2. Проведение тестов в qt 9

Литература 10

1. Сетевое программирование в qt

1.1. Обзор классов и примеры реализаций

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

Сокетное соединение – это соединение точка-точка, которое производится между двумя процессами.

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

Для дейтаграммных сокетов существует класс QUdpSocket, а для поточных – QTcpSocket. Далее мы будем рассматривать только поточные сокеты.

1.2. Клиент-Серверная архитектура(модель Клиент-Сервер)

Сервер предлагает услуги, а клиент ими пользуется. Программа, использующая сокеты, может выполнять роль сервера, либо роль клиента. Для того, чтобы клиент мог взаимодействовать с сервером, ему необходимо знать IP-адрес сервера и номер порта, через который клиент должен сообщить о себе. Когда клиент начинает соединение с сервером, его система назначает данному соединению отдельный сокет, а когда сервер принимает соединение, сокет назначается со стороны сервера. После этого устанавливается связь между этими двумя сокетами, по которой высылаются данные запроса к серверу. А сервер высылает по этому же соединению готовые результаты клиенту согласно его запросу.

1.3. Реализация Сервера

Для реализации сервера Qt предоставляет удобный класс QTcpServer, который предназначен для управления входящими TCP-соединениями.

Файл main.cpp

#include <QtGui>

#include "MyServer.h"

int main(int argc, char** argv)

{

QApplication app(argc, argv);

MyServer server(2323);

server.show();

return app.exec();

}

В данном листинге создается объект сервера. Чтобы запустить сервер, мы создаем объект, передавая в конструктор номер порта, по которому должен работать нужный нам сервис.

Примечание: необходимо, чтобы данный порт был не занят какими-либо другими сервисами и был открыт файрволом(при его наличии в системе).

Файл MyServer.h

#include <QWidget>

class QTcpServer;

class QTextEdit;

class QTcpSocket;

class MyServer : public QWidget {

Q_OBJECT

private:

QTcpServer* m_ptcpServer;

QTextEdit* m_ptxt;

quint16 m_nNextBlockSize;

private:

void sendToClient(QTcpSocket* pSocket, const QString& str);

public:

MyServer(int nPort, QWidget* pwgt = 0);

public slots:

virtual void slotNewConnection();

void slotReadClient ();

};

В классе MyServer мы объявляем атрибут m_ptcpserver, который и является основой управления нашим сервером. Атрибут m_NextBlockSizе служит для хранения длины следующего полученного от сокета блока. Многострочное текстовое поле m_ptxt предназначено для информирования о происходящих соединениях.

Файл MyServer.cpp

#include <QtNetwork>

#include <QtGui>

#include "MyServer.h"

// ----------------------------------------------------------------------

MyServer::MyServer(int nPort, QWidget* pwgt /*=0*/) : QWidget(pwgt)

, m_nNextBlockSize(0)

{

m_ptcpServer = new QTcpServer(this); //создаётся объект, адрес заносим в указатель

if (!m_ptcpServer->listen(QHostAddress::Any, nPort)) //слушаем наличие соединений по любому ip-адресу данной машины, с конкретного порта, сидим и слушаем до упора, если listen вернул 0, то это признак ошибки, попадаем сюда

//->

{

QMessageBox::critical(0, "Server Error", "Unable to start the server:" + m_ptcpServer->errorString());

m_ptcpServer->close(); //закрываем соединение

return;

}

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

connect(m_ptcpServer, SIGNAL(newConnection()),

this, SLOT(slotNewConnection())

);

m_ptxt = new QTextEdit;

m_ptxt->setReadOnly(true);

//Layout-настройки

QVBoxLayout* pvbxLayout = new QVBoxLayout;

pvbxLayout->addWidget(new QLabel("<H1>Server</H1>"));

pvbxLayout->addWidget(m_ptxt);

setLayout(pvbxLayout);

}

Для запуска сервера нам необходимо вызвать в конструкторе метод listen(). В этот метод необходимо передать номер порта, который мы получили в конструкторе. При возникновении ошибочных ситуаций(невозможность захвата порта), этот метод возвратит false, на который мы отреагируем показом окна с сообщением об ошибке. Если ошибки не произошло, мы соединяем определенный нами слот slotNewConnection() с сигналом newConnection(), который высылается при каждом присоединении нового клиента.

// ----------------------------------------------------------------------

void MyServer::slotNewConnection() //обработчик нового соединения

{

// получили адрес сокета (корзинки) от метода nextPC, в которую будут / помещаться байты

QTcpSocket* pClientSocket = m_ptcpServer->nextPendingConnection();

//связали

connect(pClientSocket, SIGNAL(disconnected()),

pClientSocket, SLOT(deleteLater())

);

// если прошло соединение, мы готовы читать данные от клиента, вызываем метод //по чтению данных

connect(pClientSocket, SIGNAL(readyRead()),

this, SLOT(slotReadClient())

);

//вызов НАШЕГО метода, который шлёт клиенту ответ сервера

sendToClient(pClientSocket, "Server Response: Connected!");

}

Данный метод вызывается каждый раз при соединении с новым клиентом. Для подтверждения соединения с клиентом необходимо вызвать метод nextPendingConnection(), который возвратит сокет, посредством которого можно осуществлять дальнейшую связь с клиентом. Дальнейшая работа сокета обеспечивается сигнально-слотовыми связями. Мы соединяем сигнал disconnected(), высылаемый сокетом при отсоединении клиента, со стандартным слотом QObject::deleteLater(), предназначенного для последующего его уничтожения. При поступлении запросов от клиентов высылается сигнал readyToRead(), который мы соединяем со слотом slotReadClient().

// ----------------------------------------------------------------------

void MyServer::slotReadClient()

{

// приводим указатель, возвращённый сендером к нужному типу и заносим в свою // переменную-указатель

QTcpSocket* pClientSocket = (QTcpSocket*)sender();

//создаём поток входной на основе сокета, см. конструктор датастрима

QDataStream in(pClientSocket);

//бесконечный цикл, с выходом по брейку

for (;;)

{

//m_nNextBlockSize хранит размер блока, переданного от клиента

if (!m_nNextBlockSize) //в ветку попадаем, если блоксайз=0

{

//если доступное число байт меньше размера этой переменной (16 //бит или 2 байта), то выходим из цикла for

if (pClientSocket->bytesAvailable() < sizeof(quint16))

{ break; }

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

in >> m_nNextBlockSize;

}

//ждём, пока в сокете не накопится нужное количество байт, если байт в //сокете меньше, чем размер блока, то выходим, поскольку данных там //недостаточно. Если мало - выход из for’a.

if (pClientSocket->bytesAvailable() < m_nNextBlockSize)

{

break;

}

//если все проверки пройдены, то создаем объект, хранящий время и объект-//строку.

QTime time;

QString str;

//из потока направляем набор байт в ВРЕМЯ (отправки клиентом) и в СТРОКУ //(переданную клиентом)

in >> time >> str;

QString strMessage =

time.toString() + " " + "Client has sended - " + str;

//добавляем сформированную строку к уже отображённым строкам в редакторе.

m_ptxt->append(strMessage);

//когда всё получили, обнулили размер блока, чтобы ждать следующий посыл

m_nNextBlockSize = 0;

//клиенту отправили подтверждение, что мы от него получили

sendToClient(pClientSocket,

"Server Response: Received \"" + str + "\""

); }}

Каждый переданный сокетом блок начинается полем полем размера блока. Размер блока считывается при условии, что размер полученных данных не меньше двух байт(тип QString, Unicode). Вызовом метода sendToClient() мы сообщаем клиенту о том, что нас успешно удалось прочитать высланные им данные.

// ----------------------------------------------------------------------

void MyServer::sendToClient(QTcpSocket* pSocket, const QString& str)

{

//создаём массив из байтов (последовательность, с которой мы работаем)

QByteArray arrBlock;

//связываем поток с массивом байтов (передаём адрес массива)

QDataStream out(&arrBlock, QIODevice::WriteOnly);

//в поток помещаем двухбайтное слово, которое будет хранить размер, потом //туда заносим объект-ВРЕМЯ, созданный методом currentTime(), и саму строку-//ответ

out << quint16(0) << QTime::currentTime() << str;

//ищем в потоке значение 0, чтобы...

out.device()->seek(0);

//...записать туда общее количество байт, записанное в поток, минус 2 байта, //отведённые под хранение собственно количества

out << quint16(arrBlock.size() - sizeof(quint16));

pSocket->write(arrBlock); //заносим последовательность байт в сокет

}

В этом методе мы формируем данные, которые будут отосланы клиенту.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]