4 / 4 / 1
Регистрация: 08.01.2011
Сообщений: 46
1

Проблема с поддержанием соединения с сервером

10.12.2011, 11:04. Показов 2171. Ответов 7
Метки нет (Все метки)

Здравствуйте, Уважаемые участники форума. Пишу клиент-серверное приложение. Возникает проблема при поддержке соединения между клиентом и сервером. Нужно, чтобы клиент и сервер постоянно держали соединение.

У меня при запуске сервера все хорошо, а при запуске клиента, он повисает в памяти (окно не отображается с данными). При закрытии сервера, окно появляется вместе с данными, которые передал сервер. Если же сделать tcpSocket->close(); то все хорошо. И сервер работает и клиент отображает данные после запуска. Но по нажатии кнопки обновить, все данные пропадают с окна, поскольку сокет был закрыт. Подскажите как быть? Ниже привожу код сервера и клиента.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*Сервер*/
#include "dlginfo.h"
#include "ui_dlginfo.h"
 
DlgInfo::DlgInfo(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DlgInfo)
{
    ui->setupUi(this);
//    setWindowTitle(QString::fromUtf8("Сервер"));
    setWindowTitle(tr("Сервер"));
 
    StartServer();
 
    connect(ui->pbtn_Start, SIGNAL(clicked()), SLOT(StartServer()));
    connect(ui->pbtn_Stop, SIGNAL(clicked()), SLOT(StopServer()));
}
 
void DlgInfo::StartServer()
{
    tcpServer = new QTcpServer(this);
 
    if(!tcpServer->listen(QHostAddress::Any, 3333))
    {
        ui->txtbrsr_Info->append(tcpServer->errorString());
    }
 
    else
    {
        connect(tcpServer, SIGNAL(newConnection()), SLOT(IncKey()));
        ui->txtbrsr_Info->append(tr("Сервер запущен"));
        ui->pbtn_Start->setDisabled(true);
    }
}
 
void DlgInfo::StopServer()
{
    tcpServer->close();
    ui->txtbrsr_Info->append(tr("Сервер остановлен"));
    ui->pbtn_Start->setEnabled(true);
}
 
void DlgInfo::NewIncConn()
{
    //Информация о подключаемых клиентах
    ui->txtbrsr_Info->append(tr("Входящее соединение"));
}
 
void DlgInfo::IncKey()                //Ждем задание от клиента
{
    NewIncConn();
    tcpSocket = tcpServer->nextPendingConnection();
    tcpSocket->waitForReadyRead();
    QByteArray Key = tcpSocket->readAll();
 
    if(Key == "qryJrnl_SELECT")
    {
        JrnlRefresh();
    }
}
 
void DlgInfo::JrnlRefresh()
{
//    NewIncConn();
//    tcpSocket = tcpServer->nextPendingConnection();
    tcpSocket->write("Ready");                          //Отправляем клиенту сообщение о готовности
 
    tcpSocket->waitForReadyRead();
    QByteArray JrnlSelect = tcpSocket->readAll();       //Считали строку запроса целиком
 
    QSqlQuery qryJrnl_SELECT;
    qryJrnl_SELECT.exec(JrnlSelect);
    qryJrnl_SELECT.next();
 
    do{
        QByteArray pkJrnlID = qryJrnl_SELECT.value(0).toByteArray();
        QString NameJrnl = qryJrnl_SELECT.value(1).toString();
 
        qint32 sl_pkJrnlID = pkJrnlID.size();           //Получаем длину строки, передаваемой клиенту. Делаем для разделения данных, чтобы не пришла "каша"
        tcpSocket->write((char*) &sl_pkJrnlID,4);       //Передаем длину строки клиенту
        tcpSocket->write(pkJrnlID);                     //Передаем id журнала клиенту
 
        qint32 sl_NameJrnl = NameJrnl.toUtf8().size();  //Снова получаем длину строки и не забываем, что передаем ее в utf8, иначе придет "каша"
        tcpSocket->write((char*) &sl_NameJrnl,4);       //Передаем длину строки клиенту
        tcpSocket->write(NameJrnl.toUtf8().data());     //Передаем наименование журнала
 
//        qDebug() << pkJrnlID << NameJrnl;
 
    }while(qryJrnl_SELECT.next());     //Выполняем запрос до тех пор пока не закончатся данные
    tcpSocket->close();                   //Если не закрыть сокет клиент повиснет
}
 
DlgInfo::~DlgInfo()
{
    tcpServer->close();
    delete ui;
}

Вот код клиента:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*Клиент*/
void MainWindow::fncJournal_Refresh()            //Эта функция вызывается по нажатии кнопки "Обновить"
{
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");  //Указываем кодировку
 
    QByteArray SendKey = ("qryJrnl_SELECT");
    tcpSocket->write(SendKey);                  //Отправляем серверу задание для выполнения запроса
 
    tcpSocket->waitForReadyRead();
    tcpSocket->readAll();                       //Ждем ответа от сервера о готовности
 
    QByteArray qryJrnl_SELECT = ("SELECT pkJrnlID, NameJrnl  FROM rJrnl_tbl ORDER BY NameJrnl DESC");
    tcpSocket->write(qryJrnl_SELECT);
 
 
    QTableWidgetItem *newItem;
    ui->tblwgt_Journal->setRowCount(0);
    ui->tblwgt_Journal->setSortingEnabled(true);
    do
    {
        tcpSocket->waitForReadyRead();
        qint32 sl_pkJrnlID;                                     //Длина принимаемой строки, sl - String Length
        tcpSocket->read((char*) &sl_pkJrnlID,4);                //Читаем длину строки, от сервера
        QByteArray pkJrnlID = tcpSocket->read(sl_pkJrnlID);     //Читаем, ТОЛЬКО, необходимое количество байт, чтобы отделить pkJrnlID от NameJrnl
//        qDebug() << pkJrnlID;
 
        tcpSocket->waitForReadyRead();
        qint32 sl_NameJrnl;
        tcpSocket->read((char*) &sl_NameJrnl,4);
        QByteArray NameJrnl = tcpSocket->read(sl_NameJrnl);
        QString u_NameJrnl = codec->toUnicode(NameJrnl);        //Указываем кодировку, явным образом, чтобы избежать проблем при отображении кириллицы
//        qDebug() << u_NameJrnl;
 
        ui->tblwgt_Journal->insertRow(0);
        newItem = new QTableWidgetItem();newItem->setText(pkJrnlID);
        ui->tblwgt_Journal->setItem(0,0, newItem);
        newItem = new QTableWidgetItem();newItem->setText(u_NameJrnl);
        ui->tblwgt_Journal->setItem(0,1, newItem);
    }while(tcpSocket->bytesAvailable() != 0);
 
    ui->tblwgt_Journal->hideColumn(0);
    ui->tblwgt_Journal->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
    ui->tblwgt_Journal->horizontalHeader()->setStretchLastSection(true);
    ui->tblwgt_Journal->verticalHeader()->setHidden(true);
 
    int RowCnt = ui->tblwgt_Journal->rowCount();
 
    if(RowCnt == 0) {ui->statusBar->showMessage(tr("Записей в базе: 0"));}
 
    else {ui->statusBar->showMessage(tr("Записей в базе: ") + QString::number(RowCnt));}
 
//    tcpSocket->waitForBytesWritten();
    tcpSocket->close();
Заранее спасибо.
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.12.2011, 11:04
Ответы с готовыми решениями:

Установка соединения с MySql сервером
Здравствуйте! возникла проблема установки соединения с mysql сервером... вот функция которая...

Нет соединения с сервером
у меня следующая проблема. на моём компе клиент спокойно подключается и по локальному ip, и по...

Проверка соединения с сервером
Доброго всем здоровьица. (C#) В программе существует два потока: один посылает данные на сервер,...

Ошибки соединения c сервером
Проблема вот в чем, переодически пропадает соединение с mysql сервером, настроена односторонняя...

7
Почетный модератор
11271 / 4222 / 429
Регистрация: 12.06.2008
Сообщений: 12,195
10.12.2011, 16:39 2
Делал бы на слотах и сигналах - не было бы проблем. wait'ы в основном потоке - это зло. Или выноси всю работу с сокетом в отдельный поток.
0
4 / 4 / 1
Регистрация: 08.01.2011
Сообщений: 46
10.12.2011, 22:38  [ТС] 3
А можно пример, как это должно быть реализовано на сигналах и слотах? Или там все нужно переделывать?
0
Почетный модератор
11271 / 4222 / 429
Регистрация: 12.06.2008
Сообщений: 12,195
10.12.2011, 23:24 4
Где-то в начале (например, в конструкторе формы) подключаешь
C++
1
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(slotTCPClientReadyRead()));
Отправлять данные можно точно так же, но принимать их уже в слоте:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void MainWindow::slotTCPClientReadyRead()
{
    do
    {
        qint32 sl_pkJrnlID;                                     //Длина принимаемой строки, sl - String Length
        tcpSocket->read((char*) &sl_pkJrnlID,4);                //Читаем длину строки, от сервера
        QByteArray pkJrnlID = tcpSocket->read(sl_pkJrnlID);     //Читаем, ТОЛЬКО, необходимое количество байт, чтобы отделить pkJrnlID от NameJrnl
//        qDebug() << pkJrnlID;
 
        qint32 sl_NameJrnl;
        tcpSocket->read((char*) &sl_NameJrnl,4);
        QByteArray NameJrnl = tcpSocket->read(sl_NameJrnl);
        QString u_NameJrnl = codec->toUnicode(NameJrnl);        //Указываем кодировку, явным образом, чтобы избежать проблем при отображении кириллицы
//        qDebug() << u_NameJrnl;
 
        ui->tblwgt_Journal->insertRow(0);
        newItem = new QTableWidgetItem();newItem->setText(pkJrnlID);
        ui->tblwgt_Journal->setItem(0,0, newItem);
        newItem = new QTableWidgetItem();newItem->setText(u_NameJrnl);
        ui->tblwgt_Journal->setItem(0,1, newItem);
    }while(tcpSocket->bytesAvailable() != 0);
}
Только тут общий подход немного неверный... TCP отправляет данные сплошным потоком. То что сейчас bytesAvailable()==0 - это ещё не значит, что данные закончились. Могла возникнуть задержка на линии или просто сервер перегружен и медленно отправляет данные. Они могут придти через пару секунд, а ты ориентируясь на bytesAvailable выходишь из цикла и закрываешь соединение.
Т.е. должен быть какой-то особый признак того, что сервер передал все данные. Хотя, в данном случае, можно сделать, что бы сервер сам закрывал соединение, когда всё передаст.
0
4 / 4 / 1
Регистрация: 08.01.2011
Сообщений: 46
11.12.2011, 02:50  [ТС] 5
У меня на стороне сервера проблема. У него не выполняется функция, которая должна держать соединение. Мне так кажется... Или я не прав?
Ведь при старте сервера и клиента (первый запуск клиента) все работает, а при нажатии кнопки обновить на клиенте, сервер уже не отправляет данные.

Добавлено через 2 часа 42 минуты
Переделал сервер с сигналами и слотами, но не пойму, как мне избежать tcpSocket->close(); и дальнейшего зависания клиента в памяти.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "dlginfo.h"
#include "ui_dlginfo.h"
 
DlgInfo::DlgInfo(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DlgInfo)
{
    ui->setupUi(this);
//    setWindowTitle(QString::fromUtf8("Сервер"));
    setWindowTitle(tr("Сервер"));
 
    StartServer();
 
    connect(ui->pbtn_Start, SIGNAL(clicked()), SLOT(StartServer()));
    connect(ui->pbtn_Stop, SIGNAL(clicked()), SLOT(StopServer()));
}
 
void DlgInfo::StartServer()
{
    tcpServer = new QTcpServer(this);
 
    if(!tcpServer->listen(QHostAddress::Any, 3333))
    {
        ui->txtbrsr_Info->append(tcpServer->errorString());
    }
 
    else
    {
        connect(tcpServer, SIGNAL(newConnection()), SLOT(AcceptConn()));    //Тут мы создаем соединение
        ui->txtbrsr_Info->append(tr("Сервер запущен"));
        ui->pbtn_Start->setDisabled(true);
    }
}
 
void DlgInfo::StopServer()
{
    tcpServer->close();
    ui->txtbrsr_Info->append(tr("Сервер остановлен"));
    ui->pbtn_Start->setEnabled(true);
}
 
void DlgInfo::NewIncConn()
{
    //Информация о подключаемых клиентах
    ui->txtbrsr_Info->append(tr("Входящее соединение"));
}
 
void DlgInfo::AcceptConn()   //Тут создается соединение с клиентом и ждем указаний от клиента
{
    NewIncConn();
    tcpSocket = tcpServer->nextPendingConnection();
    connect(tcpSocket, SIGNAL(readyRead()), SLOT(IncKey()));
}
 
void DlgInfo::IncKey()           //тут указания от клиента
{
        QByteArray Key = tcpSocket->readAll();
 
        if(Key == "qryJrnl_SELECT")
        {
            JrnlRefresh();return;
        }
}
 
void DlgInfo::JrnlRefresh()
{
    tcpSocket->write("Ready");                          //Отправляем клиенту сообщение о готовности
 
    tcpSocket->waitForReadyRead();
    QByteArray JrnlSelect = tcpSocket->readAll();       //Считали строку запроса целиком
 
    QSqlQuery qryJrnl_SELECT;
    qryJrnl_SELECT.exec(JrnlSelect);
    qryJrnl_SELECT.next();
 
    do{
        QByteArray pkJrnlID = qryJrnl_SELECT.value(0).toByteArray();
        QString NameJrnl = qryJrnl_SELECT.value(1).toString();
 
        qint32 sl_pkJrnlID = pkJrnlID.size();           //Получаем длину строки, передаваемой клиенту. Делаем для разделения данных, чтобы не пришла "каша"
        tcpSocket->write((char*) &sl_pkJrnlID,4);       //Передаем длину строки клиенту
        tcpSocket->write(pkJrnlID);                     //Передаем id журнала клиенту
 
        qint32 sl_NameJrnl = NameJrnl.toUtf8().size();  //Снова получаем длину строки и не забываем, что передаем ее в utf8, иначе придет "каша"
        tcpSocket->write((char*) &sl_NameJrnl,4);       //Передаем длину строки клиенту
        tcpSocket->write(NameJrnl.toUtf8().data());     //Передаем наименование журнала
 
//        qDebug() << pkJrnlID << NameJrnl;
 
    }while(qryJrnl_SELECT.next());     //Выполняем запрос до тех пор пока не закончатся данные
}
 
DlgInfo::~DlgInfo()
{
    tcpServer->close();
    delete ui;
}
Народ подскажите, как мне не разрывать соединения с сервером и по нажатии кнопки "Обновить" на клиенте, чтобы данные в нем обновлялись. Не понимаю. Посмотрите исходник, плиз. Очень нужно.

Я так понимаю, каким-то образом нужно вызывать функцию IncKey(). А если закрывать сокеты, то надо вызывать tcpSocket = tcpServer->nextPendingConnection(). Если я правильно понимаю.

Заранее спасибо.
0
1664 / 1133 / 80
Регистрация: 21.08.2008
Сообщений: 4,725
Записей в блоге: 1
11.12.2011, 23:19 6
Посмотри в примерах как правильно писать клиент-серверные приложение. Пример называется FortuneServer.
В кратце: стартуем у сервера QTcpServer::listen(...) ждем соединения, при подключении передается дескриптор сокета, создаем в сервере новый сокет, присваиваем ему этот дескриптор и работаем. При этом, что бы соединение не разрывалосm не надо сокету (ни на сервере ни на клиенте) вызывать disconnctFromHost или close
0
4 / 4 / 1
Регистрация: 08.01.2011
Сообщений: 46
12.12.2011, 01:29  [ТС] 7
Посмотри в примерах как правильно писать клиент-серверные приложение. Пример называется FortuneServer.
В кратце: стартуем у сервера QTcpServer::listen(...) ждем соединения, при подключении передается дескриптор сокета, создаем в сервере новый сокет, присваиваем ему этот дескриптор и работаем. При этом, что бы соединение не разрывалосm не надо сокету (ни на сервере ни на клиенте) вызывать disconnctFromHost или close
oxotnik, честное слово, если бы я хорошо разбирался в Qt и в сокетах вообще, то не стал бы писать в этот форум. FortuneServer и FortuneClient уже не однократно мной просматривались, но там мне мало, что, понятно. Я же выложил выше в исходнике, то что я сумел сделать. Неужели нельзя просто показать, в то место, где у меня проблема и подсказать?

Тем более, что эта работа, просто моя контрольная в универ.

Как я уже писал выше. Если у меня не вызывать tcpSocket->close(), то клиент повисает, где-то в памяти и окно не отображается. Как только стоит завершить работу сервера, окно клиента, тут же показывается на экран с данными.

Народ, пусть у меня будет использоватьсяtcpSocket->close() при выполнении любого запроса к серверу (подобно веб-серверу), но какую функцию мне написать, чтобы поднимать новое соединение при завершении чтения функции IncKey().
0
1664 / 1133 / 80
Регистрация: 21.08.2008
Сообщений: 4,725
Записей в блоге: 1
12.12.2011, 09:06 8
Цитата Сообщение от belirafor Посмотреть сообщение
Если у меня не вызывать tcpSocket->close(), то клиент повисает
потому что надо на асинхронных сокетах делать, т.е. обрабатывать сигналы а не ждать событий в основном цикле (на клиенте)
Смотри в справке как реализован Threaded Fortune Server Example
там же (в справке) смотри как реализован клиент.
ну если очень сложно отказаться от waitFor... тогда делай в отдельном потоке работу с сетью
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
12.12.2011, 09:06
Помогаю со студенческими работами здесь

Соединения клиента с сервером
Доброго времени суток. Недавно начил изучать WinSock и у меня возник вопрос как соединить клиент...

Ошибка соединения с сервером
Добрый вечер. Подскажите, пожалуйста что за ошибка такая. ЗАГОЛОВОК: Соединение с сервером...

Ошибка соединения с сервером
Привет! Я купил себе недорогой хостинг и спросил у т.п. выдержит ли он 1000-2000 запросов в минуту...

Ошибка соединения с сервером
Решил создать сайт на движке Wordpress, зарегистрировал домен и хостинг, выгрузил движок на сервер,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru