29 October 2011

Простой сервер на Qt/C++

C++
В последнее время очень часто приходится слушать определенный порт, получать данные от клиента и отправлять соответствующий ответ. Решил поделиться с новичками, как же создать такой сервер и решить некоторые поставленные вопросы.
В этой статье мы рассмотрим:
— Создание tcp сервера.
— Подключение нескольких клиентов к серверу параллельно.
— Отключение клиентов (отключение сокетов).
— Получение и отправку данных.

Исходники: https://github.com/valualit/QTcpServer01

image


QTcpServer или слушаем нужный порт

В переменной server_status — храню статус QTcpServer, чтоб не происходило эксцессов при работе сервера (если 0 — то сервер не слушает порт, 1 — слушает).
Сигналы в данном случае решают лишний раз проблему с прослушиванием порта, т.е. слот newuser() в данный момент вызывается только тогда, когда появляется новое подключение к серверу.
    tcpServer = new QTcpServer(this);
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newuser()));
    if (!tcpServer->listen(QHostAddress::Any, 33333) && server_status==0) {
        qDebug() <<  QObject::tr("Unable to start the server: %1.").arg(tcpServer->errorString());
    } else {
        server_status=1;
        qDebug() << QString::fromUtf8("Сервер запущен!");
    }


Подключение нескольких клиентов к серверу параллельно

Код ниже демонстрирует, как создать новый сокет и заставить его слушать сигналы. Так же в нем получаем дескриптор сокета, который используем в качестве ключа для хранения объекта сокета, который пригодится нам при дальнейшей работе.
    if(server_status==1){
        qDebug() << QString::fromUtf8("У нас новое соединение!");
        QTcpSocket* clientSocket=tcpServer->nextPendingConnection();
        int idusersocs=clientSocket->socketDescriptor();
        SClients[idusersocs]=clientSocket;
        connect(SClients[idusersocs],SIGNAL(readyRead()),this, SLOT(slotReadClient()));
    }


QMap<int,QTcpSocket *> SClients; Данная карта хранит объекты созданных сокетов. Ее использую например если принудительно останавливаю сервер и мне необходимо закрыть открытые сокеты. Если их не закрыть, то клиент будет еще долго ждать ответ от нашего сервера и не закрывать соединение. Ниже выложен вариант принудительного закрытия всех сокетов.
    if(server_status==1){
        foreach(int i,SClients.keys()){
            QTextStream os(SClients[i]);
            os.setAutoDetectUnicode(true);
            os << QDateTime::currentDateTime().toString() << "\n";
            SClients[i]->close();
            SClients.remove(i);
        }
        tcpServer->close();
        qDebug() << QString::fromUtf8("Сервер остановлен!");
        server_status=0;
    }


При создании нового сокета Вы уже наверно заметили сигнал readyRead(), он выполняется когда клиент передает какие-то данные на наш сервер, в этот момент мы и будем давать ответ нашему клиенту, предварительно получив данные.

    // Получаем объект сокета, который вызвал данный слот
    QTcpSocket* clientSocket = (QTcpSocket*)sender();
    // Получаем дескриптор, для того, чтоб в случае закрытия сокета удалить его из карты
    int idusersocs=clientSocket->socketDescriptor();
    // Пример отправки ответа клиенту
    QTextStream os(clientSocket);
    os.setAutoDetectUnicode(true);
    os << "HTTP/1.0 200 Ok\r\n"
          "Content-Type: text/html; charset=\"utf-8\"\r\n"
          "\r\n"
          "<h1>Nothing to see here</h1>\n"
          << QDateTime::currentDateTime().toString() << "\n";
    // Полученные данные от клиента выведем в qDebug, 
    // можно разобрать данные например от GET запроса и по условию выдавать необходимый ответ. 
    qDebug() << clientSocket->readAll()+"\n\r");
    // Если нужно закрыть сокет
    clientSocket->close();
    // Удалим объект сокета из карты
    SClients.remove(idusersocs);


Таким образом мы получаем сервер (например HTTP), который слушает порт 33333, сможет обрабатывать сразу несколько запросов одновременно и отдавать нужный результат.

image

P.S. В будущем напишу о передачи большого объема данных с помощью сокетов.
Tags:QTC++QTcpServerQTcpSocket
Hubs: C++
+23
103.1k 205
Comments 27