Форум программистов, компьютерный форум, киберфорум
C++ Qt
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/16: Рейтинг темы: голосов - 16, средняя оценка - 4.75
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
1

Клиент серверное приложение как различить клиентов

27.01.2012, 18:08. Просмотров 3048. Ответов 16


Вот такую штуку нафорганил:
в конструкторе класса
C++
1
2
3
4
5
6
 
    server = new QTcpServer(this);
    srv_port =12085;
    bytes=0;
    server->listen(QHostAddress::Any, srv_port);
    connect(server, SIGNAL(newConnection()), this, SLOT(NewGamer()));
слот NewGamer
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gamer = server->nextPendingConnection();
    ui->txtInfo->append(tr("Подключился новый игрок: ") + gamer->peerAddress().toString());
    if(countGamer==0)
    {
        gamer->write("NO");
        countGamer ++;
    }
    else
    {
        gamer->write("YES");
        countGamer ++;
    }
    connect(gamer, SIGNAL(readyRead()), this, SLOT(GetMessage()));
    connect(gamer, SIGNAL(disconnected()), gamer, SLOT(disconnectFromHostImplementation()));
встал вопрос,как мне понять от кокого клиента я получил данные и кому я отправил, что бы не получилось что клиент1 отправил данные а я ему же их вернул
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
27.01.2012, 18:08
Ответы с готовыми решениями:

Клиент-серверное приложение: как лучше сделать?
Мне необходимо реализовать следующее: я так понимаю, что это будет серверное приложение (если...

Многопоточный клиент-серверное приложение
Здравствуйте! Пишу программу, многопоточное клиент-серверное приложение, подскажите пожалуйста,...

Клиент-серверное приложение "Удаленный проводник" приложение на QT под Linux
Как в данном проводнике использовать функции qdir и qfile? Как составить клиенту запрос на сервер,...

Клиент-серверное приложение. Одновременный запрос клиентов
Приложения нормально работают с одним клиентом при не слишком частых запросах (интервал между...

16
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
27.01.2012, 18:22 2
ну, у тебя же при подключении двух клиентов создается два QTcpSocket'а. как ты их можешь спутать?) если планируется более двух игроков, то заведи таблицу, где какому-то признаку (имя или номер) будет ставиться в соответствие сокет. и, соответственно, отправитель в сообщении укажет "признак" получателя, а сервер на основе полученного "признака" решит, в какой именно сокет писать сообщение.

кроме того, у каждого сокета есть уникальный дескриптор.
C++
1
int QAbstractSocket::socketDescriptor () const
2
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
27.01.2012, 20:33  [ТС] 3
все равно не понимаю , ладно буду пробовать, так быстрее дойдет, но если есть пример кода более детально описывающий работу, буду благодарен
0
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
27.01.2012, 22:05 4
в общем, у меня немного не та проблема была, поэтому кода полностью нет.
но примерно так:

QMyTcpServerClass.h
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
#ifndef QMYTCPSERVERCLASS_H
#define QMYTCPSERVERCLASS_H
 
#include <QTcpServer>
#include "QMyServerStructure.h"
 
class QMyServerStructure;    
 
class QMyTcpServerClass : public QObject {
    Q_OBJECT
    public:
        QMyTcpServerClass(int port, bool output, QMyServerStructure* server, QObject* parent=0);
    private:
        QTcpServer *pTcpServer;                
        QMyServerStructure* pServerStructure; 
        int serverPort; 
        bool enableOutput;     //флаг отладочной печати
    private slots:
        /*Слот, принимающий и обрабатывающий новое соединение.
          Создает сокет для соединения и настраивает другие слоты на его сигналы*/
        void slotNewConnection();
 
        /*Слот, обрабатывающий входящее сообщение*/
        void slotNewIncomingMessage();
 
        /*Слот, обрабатывающий исходящее сообщение (отправляет пользователю)
           In: msg - сообщение для отправки пользователю
                sckt - сокет, настроенный на пользователя*/
        void slotNewOutcomingMessage(QMyMessageClass* msg, QTcpSocket* sckt);
 
        void slotDisconnectSignal();
 
        void errorHandler();
    signals:
        /*Сигнал, о наличии готового для дальнейшей обработки входящего сообщения
           In: msg - сформированное входящее сообщение
                sckt - сокет, принявший сообщение*/
        void messageReady(QMyMessageClass* msg, QTcpSocket* sckt);
 
        void clientDisconnected(QTcpSocket*);
};
 
 
#endif // QMYTCPSERVERCLASS_H
реализация QMyTcpServerClass.cpp
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
#include "QMyTcpServerClass.h"
#include <QtCore>
#include <QtNetwork/QTcpServer>
 
QMyTcpServerClass::QMyTcpServerClass(int port, bool output, QMyServerStructure *server, QObject *parent):QObject() {
    enableOutput=output;
    if (enableOutput) {
        qDebug()<<"------------------------------------------------------------------------------";
        qDebug()<<"Initializing TCP server";
    }
    pServerStructure=server;
    serverPort=port;
    pTcpServer=new QTcpServer;
    /*следующее соединение обеспечивает пересылку любого приянтого сообщения на обработку в класс QMyServerStructure*/  connect(this,SIGNAL(messageReady(QGMMessageClass*,QTcpSocket*)),pServerStructure,SLOT(processClientQuery(QGMMessageClass*,QTcpSocket*)));
    /*следующее соединение обрабатывает каждое новое подключение к серверу*/
    connect(pTcpServer,SIGNAL(newConnection()),this,SLOT(slotNewConnection()));
 
    pTcpServer->listen(QHostAddress::Any,serverPort);
}
 
//Слот, принимающий новое соединение.
void QMyTcpServerClass::slotNewConnection() {
    //получаем сокет из входящих соединений
    QTcpSocket* pSocket=pTcpServer->nextPendingConnection();
    
    //в случае непредвиденного разъединения
    connect(pSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnectSignal()));
    connect(pSocket,SIGNAL(disconnected()),pSocket,SLOT(deleteLater()));
    
    //соединение, обеспечивающее принятие сообщения сокетом и формирования объекта QMyMessageClass
    connect(pSocket,SIGNAL(readyRead()),this,SLOT(slotNewIncomingMessage()));
 
    //в случае ошибок
    connect(pSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(errorHandler()));
}
 
//обработка поступившего сообщения
void QMyTcpServerClass::slotNewIncomingMessage() {
    //определяем, каким сокетом принято сообщение
    QTcpSocket* pSocket=(QTcpSocket*)sender();
 
    //формируем объект класса QMyMessageClass
    QDataStream in(pSocket);
    in.setVersion(QDataStream::Qt_4_7);
    QMyMessageClass* pMessage = new QMyMessageClass(in);
    pMessage->setSocket(pSocket);
 
    //сигнализируем, что имеется новое принятое сообщение и передаем его на обработку серверу
    emit messageReady(pMessage,pSocket);
}
 
//отсылаем сообщение msg, используя сокет sckt
void QMyTcpServerClass::slotNewOutcomingMessage(QMyMessageClass *msg,QTcpSocket* sckt) {
    QByteArray messageBlock;
    QDataStream out(&messageBlock,QIODevice::ReadWrite);
   
    out.setVersion(QDataStream::Qt_4_7);
    out<<msg->getLength()<<msg->getCode()<<msg->getSession()<<msg->getPlace();
    out<<msg->getTimeObject();
    out<<msg->getBitsFieldObject();
    out<<msg->getDataObject();
    out.device()->seek(0);
    sckt->write(messageBlock);
 
    delete msg;
}
 
void QMyTcpServerClass::slotDisconnectSignal() {
    QTcpSocket* pSocket=(QTcpSocket*)sender();
    emit clientDisconnected(pSocket);
}
 
void QMyTcpServerClass::errorHandler() {
    QTcpSocket* pSocket=(QTcpSocket*)sender();
    qDebug()<<pSocket->errorString();
}
ну, и пояснение. суть работы в том, что класс QMyTcpServerClass принимает соединения и для каждого динамически создает и настраивает на свои слоты новые сокеты (слот slotNewConnection() ). Когда на сокет приходит сообщение, на нем инициируется сигнал readyRead(), связанный со слотом slotNewIncomingMessage(). В нем поток данных из сокета "упаковывается" в специальный класс для представления сообщений QMyMessageClass. После этого сообщение передается на обработку собственно логике сервера в другой класс (QMyServerStructure).
Логика сервера опять же формирует сообщение QMyMessageClass и посредством соединения со слотом slotNewOutcomingMessage(QMyMessageClass *msg,QTcpSocket* sckt) отсылает его нужному сокету (то есть нужному игроку).

И вот именно в логике сервера тебе нужно запоминать сокеты (их адреса), а формат входящих сообщений должен явно указывать на то, кому будет адресован ответ. К примеру, при первом соединении, игрок передает сообщением свое имя. Логика сервера заносит имя в таблицу типа:
|Имя_игрока|QTcpSocket*|
После этого игрок начинает слать сообщения типа: |Имя_игрока|Какие-то_данные|. Логика сервера что-то делает с данными, формирует сообщение QMyMessageClass, находит в вышеприведенной таблице <Имя_игрока> и соответствующий ему указатель на сокет, и используя слот slotNewOutcomingMessage отсылает ответ нужному адресату. Если же у тебя всего два игрока, то вообще никаких имен передавать не надо - тут уже нет неопределенности, кому передавать ответ.

не самая лучшая реализация, правда. в частности, тут объекты класса QMyMessageClass фактически кочуют от класса к классу и нужно внимательно следить, чтобы удалять их когда нужно, а так же в твоем случае придется следить за тем, чтобы в таблице не содержались удаленные сокеты, а не то программа рухнет при попытке обращения. для сигнализации об удалении как раз имеется сигнал clientDisconnected();
2
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
27.01.2012, 22:49  [ТС] 5
Подскажите еще, как сделать лимит подключения к серверу, например 10, и 11 отказывали в подключении

Добавлено через 3 минуты
все разобрался
0
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 13:53  [ТС] 6
возник др вопрос, короче я разобрался как и что кому отправлять, но касяк такой что сервер принимает сообщения только от игрока который последний подключился, при этом игнорирует игрока который подключился первый...
0
Эксперт С++
1933 / 1045 / 109
Регистрация: 29.03.2010
Сообщений: 3,167
30.01.2012, 14:20 7
эм... проходите по всем соединениям и анализируйте их, а ещё лучше в слоте просто анализируйте от какого клиента пришло чего-то

Добавлено через 12 секунд
*в слоте чтения данных
0
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 14:48  [ТС] 8
Цитата Сообщение от l_a_m Посмотреть сообщение
эм... проходите по всем соединениям и анализируйте их, а ещё лучше в слоте просто анализируйте от какого клиента пришло чего-то

Добавлено через 12 секунд
*в слоте чтения данных
так в этом и косяк что слот чтения данных на сервер молчит молчит
конструктор класса сервера
C++
1
2
3
4
5
6
ui->setupUi(this);
    server = new QTcpServer(this);
    srv_port =12085;
    bytes=0;
    server->listen(QHostAddress::Any, srv_port);
    connect(server, SIGNAL(newConnection()), this, SLOT(NewGamer()));
Реализация слота NewGamer
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
gamer = server->nextPendingConnection();
        connect(gamer, SIGNAL(readyRead()), this, SLOT(GetMessage()));
        ui->txtInfo->append(tr("Подключился новый игрок: ") + gamer->peerAddress().toString());
        if(countGamer==0)
        {
            gamer->write("NO;0");
            gamer->setObjectName(QString("%1").arg(0));
            ui->txtInfo->append("ID_Gamer: " + gamer->objectName());
            tableGamer.insert("0", gamer);
            countGamer ++;
        }
        else if(countGamer==1)
        {
            data.clear();
            data.insert(0,"YES;1;");
 
            if(ui->tableWidget->item(0,3)->text() == "X")
            {
                data.insert(data.size(),"O");
            }
            else
            {
                data.insert(data.size(),"X");
            }
            gamer->write(data);
            gamer->setObjectName(QString("%1").arg(1));
            ui->txtInfo->append("ID_Gamer: " + gamer->objectName());
            tableGamer.insert("1", gamer);
            countGamer ++;
        }
Реализация слота GetMessage
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
 if(gamer->bytesAvailable() >0)
    {
        QStringList lst;
        data.clear();
        data = gamer->readAll();
        QString str(data);
        qDebug()<< "SERVER GRT DATA FROM GAMER: " << str;
        if(str.startsWith("PRES"))
        {
            str.replace("PRES","");
            lst = str.split(";");
            ui->txtInfo->append(QString("Игрок %1:").arg(countGamer));
            ui->txtInfo->append(QString("Играет за: %1").arg(lst.at(2)));
            ui->txtInfo->append(QString("Идентификатор: %1").arg(lst.at(0)));
            ui->txtInfo->append(QString("Имя: %1").arg(lst.at(1)));
 
            if(countGamer==1)
            {
                qDebug()<<"Connected gamer 1";
                ui->tableWidget->setItem(0, 0, new QTableWidgetItem(lst.at(1)));
                ui->tableWidget->setItem(0, 1, new QTableWidgetItem(gamer->peerAddress().toString()));
                ui->tableWidget->setItem(0, 2, new QTableWidgetItem(QString("%1").arg(srv_port)));
                ui->tableWidget->setItem(0, 3, new QTableWidgetItem(lst.at(2)));
                ui->tableWidget->setItem(0, 4, new QTableWidgetItem(0));
                gamer_1=lst.at(2);
            }
            else
            {
                qDebug()<<"Connected gamer 2";
                ui->tableWidget->setItem(1, 0, new QTableWidgetItem(lst.at(1)));
                ui->tableWidget->setItem(1, 1, new QTableWidgetItem(gamer->peerAddress().toString()));
                ui->tableWidget->setItem(1, 2, new QTableWidgetItem(QString("%1").arg(srv_port)));
                ui->tableWidget->setItem(1, 3, new QTableWidgetItem(lst.at(2)));
                ui->tableWidget->setItem(1, 4, new QTableWidgetItem(0));
            }
            data.clear();
        }
        else if(str.startsWith("STEP"))
        {
           // gamer->write(data);
            str.replace("STEP","");
            qDebug()<<"STEP:   " <<str;
            QTcpSocket *tmpGamer = new QTcpSocket(this);
            switch(str.split(";").at(0).toInt())
            {
            case 0:
                tmpGamer=tableGamer.value("1");
                tmpGamer->write(data);
                break;
            case 1:
                tmpGamer=tableGamer.value("0");
                tmpGamer->write(data);
                break;
            }
            ui->txtInfo->append(str);
            qDebug()<< "USER " << str.split(";").at(0) << " STEP: " << str;
 
        }
в итоге метод GetMessage молчит для всех кроме последнего подключившегося
0
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
30.01.2012, 14:58 9
а откуда в слоте GetMessage берется значение указателя gamer? если через поля класса, то ничего удивительного - он же переписывается в слоте newGamer при каждом новом соединении. нужно через sender() его получать.
1
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 15:04  [ТС] 10
Цитата Сообщение от Paporotnik Посмотреть сообщение
а откуда в слоте GetMessage берется значение указателя gamer? если через поля класса, то ничего удивительного - он же переписывается в слоте newGamer при каждом новом соединении. нужно через sender() его получать.
все просто:
C++
1
2
QTcpSocket *tmpGamer = new QTcpSocket(this);
tmpGamer=tableGamer.value("1");
где tableGamer контейнер:
C++
1
QHash <QString, QTcpSocket *> tableGamer;
Добавлено через 1 минуту
суть в том что данные нужно получить от одного игрока а передать другому
0
Эксперт С++
1933 / 1045 / 109
Регистрация: 29.03.2010
Сообщений: 3,167
30.01.2012, 15:43 11
у нас в проекте соединение идёт так:
C++
1
2
3
4
5
6
7
8
9
10
11
void ClientServer::incomingConnection(int nSocket) {
    ClientSocket * socket = new ClientSocket(this, nSocket);
 
    qDebug() << QString("[%1:%2] Connect")
        .arg(socket->peerAddress().toString())
        .arg(socket->peerPort());
 
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(disconnected()),                      this, SLOT(onDisconnected()));
    connect(socket, SIGNAL(request(const sxProperties &)),       this, SLOT(onRequest(const sxProperties &)));
}
не знаю с чем это связанно, может с такой же проблемой как и у Вас. (ClientSocket - это класс унаследованный от QTcpSocket)
1
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 15:54  [ТС] 12
короче лишний раз подтвердилось высказывание мамы: "Твою добрату переплюнет только твоя тупость " все работает, прав был папаратник за что ему 100500 плюсов в начале слота не добавил
C++
1
 gamer = (QTcpSocket*)sender();
0
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
30.01.2012, 16:22 13
и еще вот тут:
C++
1
2
QTcpSocket *tmpGamer = new QTcpSocket(this);
tmpGamer=tableGamer.value("1");
вроде очевидная утечка памяти. зачем создавать новый сокет, если почти тут же указатель на него затирается значением из таблицы?
0
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 16:45  [ТС] 14
Цитата Сообщение от Paporotnik Посмотреть сообщение
и еще вот тут:
C++
1
2
QTcpSocket *tmpGamer = new QTcpSocket(this);
tmpGamer=tableGamer.value("1");
вроде очевидная утечка памяти. зачем создавать новый сокет, если почти тут же указатель на него затирается значением из таблицы?
правильнее вот так:
C++
1
QTcpSocket *tmpGamer = tableGamer.value("1")
?

Добавлено через 1 минуту
хотя я вроде как родителя указал, а значит по идее при умирании родителя это тоже должно подчистится
0
Эксперт С++
1933 / 1045 / 109
Регистрация: 29.03.2010
Сообщений: 3,167
30.01.2012, 17:11 15
оно то подчистится - но вот для чего создавать объект который не будет использоваться? только ради того, что бы создать? тогда лучше в цикле создавать и итераций побольше, что бы прога ОЗУ жрала побольше -типа крутая... )
0
385 / 229 / 12
Регистрация: 06.07.2011
Сообщений: 512
30.01.2012, 17:17 16
родитель у тебя - весь класс tcp-сервера. и умрет он по окончанию программы. а новый сокет будет создаваться при каждом новом сообщении.
0
Автор FAQ
2728 / 1424 / 89
Регистрация: 08.09.2011
Сообщений: 3,746
Записей в блоге: 1
30.01.2012, 18:06  [ТС] 17
так все же как правильно ? так как я укзал во второй раз ?
C++
1
QTcpSocket *tmpGamer = tableGamer.value("1")
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.01.2012, 18:06

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.

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

Клиент-серверное приложение: как определить, что сервер/клиент не отвечает в течении определенного времени
Пишу клиент-серверное приложение. Использую TCPListener и TCPClient. Вопрос: как определить что...

Как написать клиент-серверное приложение
Как сделать так чтоб программа обменивалась данными с другой програмой через глобальную сеть....

Как организовать клиент-серверное приложение
Делаю лабораторную: необходимо написать три приложения на с#, два сервера (ServerInput и...

Как написать клиент-серверное приложение
Привет всем, хочу написать сервер.Пересмотрел кучу уроков и статей и т.д. ,но там нет подробного...

Как написать клиент-серверное приложение
Доброго времени суток. Создал аналогичную тему в раздете про си++ билдер, но там что-то тишина,...


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

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

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.