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

В переменной server_status — храню статус QTcpServer, чтоб не происходило эксцессов при работе сервера (если 0 — то сервер не слушает порт, 1 — слушает).
Сигналы в данном случае решают лишний раз проблему с прослушиванием порта, т.е. слот newuser() в данный момент вызывается только тогда, когда появляется новое подключение к серверу.
Код ниже демонстрирует, как создать новый сокет и заставить его слушать сигналы. Так же в нем получаем дескриптор сокета, который используем в качестве ключа для хранения объекта сокета, который пригодится нам при дальнейшей работе.
QMap<int,QTcpSocket *> SClients; Данная карта хранит объекты созданных сокетов. Ее использую например если принудительно останавливаю сервер и мне необходимо закрыть открытые сокеты. Если их не закрыть, то клиент будет еще долго ждать ответ от нашего сервера и не закрывать соединение. Ниже выложен вариант принудительного закрытия всех сокетов.
При создании нового сокета Вы уже наверно заметили сигнал readyRead(), он выполняется когда клиент передает какие-то данные на наш сервер, в этот момент мы и будем давать ответ нашему клиенту, предварительно получив данные.
Таким образом мы получаем сервер (например HTTP), который слушает порт 33333, сможет обрабатывать сразу несколько запросов одновременно и отдавать нужный результат.

P.S. В будущем напишу о передачи большого объема данных с помощью сокетов.
В этой статье мы рассмотрим:
— Создание tcp сервера.
— Подключение нескольких клиентов к серверу параллельно.
— Отключение клиентов (отключение сокетов).
— Получение и отправку данных.
Исходники: https://github.com/valualit/QTcpServer01

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, сможет обрабатывать сразу несколько запросов одновременно и отдавать нужный результат.

P.S. В будущем напишу о передачи большого объема данных с помощью сокетов.