QTCreator 做一个多线程TCP服务器,客户端以前别人就做好了,并定制了通信协议,现在我要用QT做一个服务器,要求能同时和多个客户机按通信协议通信。遇到了点问题,我调了很久都没解决,大家帮我看看。
通信协议是:
1. 服务器向客户机发送命令(GET);
2. 客户机发送START确认开始通信;
3. 服务器发送AGAIN允许客户机发送;
4. 接收客户机发送过来的信息并保存到缓冲区(QString)中;
5. 检索缓冲区有没有客户机发送过来的结束命令(FINISH);
6. 有:发送接收成功标志(SUCCESS)结束通信,存文件;
7. 没有:转3;
如此往复直到检索到缓冲区中有客户机发过来的结束标志。
服务器要求能同时向多个客户机发送GET命令,并发的接收客户机发过来的信息,并在收到结束标志后将收到的信息存文件,信息不能发生串扰。
我的思路:
1. 利用QT的多线程;
2. 并不是为每个连接上的用户开启一个线程,而是为每个需要通信的客户端开启一个线程;
3. 程序有GUI界面,每当有一个客户机连接到服务器以后在GUI上显示出来,并提供复选框,由用户勾选需要通信的客户端,并通过点击Get按钮向所选择的客户端发送GET命令,进行通信和信息处理;
问题是:
当我向多个客户发送GET的时候,程序偶尔会崩溃,偶尔成功但是保存的数据发生串扰(比如本来是客户1的数据却保存到了客户2的文件中)。我调了很久,没解决。
在线程的run()函数中:connect(sockfd, SIGNAL(readyRead()), this,SLOT(recvData()),Qt::DirectConnection);指定为Qt::DirectConnectio,那么recvData(),是否是在子线程之中执行?但是如果我指定为Qt::BlockingQueuedConnection程序通信依然存在问题。请问大家我的设计思想有没有问题,实现方法有没有问题。
主要代码如下,另附上源码包,http://download.csdn.net/detail/toney_ho/4058244
,大家可以下载下来调试运行一下,看是哪儿的问题,可能下载下来在QTCreator中容易看清楚点。
可能程序较长,大家耐心看一下共同进步。
- C/C++ code
//tcpserver.h#ifndef TCPSERVER_H#define TCPSERVER_H#include "tcpthread.h"#include <QTcpServer>class tcpserver : public QTcpServer{ Q_OBJECTpublic: explicit tcpserver(QObject *parent = 0); //QList<int> clientDescriptorList;signals: void newRow(int); void displayInfo(int,QString, int); void updateBar(int, qint64); void signal_send_command(int,int);public slots: void incomingConnection(int socketDescriptor); void slot_send_command(int,int);};#endif//tcpthread.h#ifndef TCPTHREAD_H#define TCPTHREAD_H#include <QThread>#include <QTcpSocket>#include <QtNetwork>#define SEND_AGIAN 2#define FOUND_FINISH 3#define SEND_SUCCESS 4#define SEND_GET 10#define FOUND_START 11#define TEST_OTHER 98#define DISPLAY_BUFFER 97#define RECV_FILE_SUCCESS 99#define SAVE_FILE_SUCCESS 100#define GET "GET"#define START "START"#define AGAIN "AGIAN"#define FINISH "FINISH"#define SUCCESS "SUCCESS"class QFile;class QTcpSocket;class TcpThread : public QThread{ Q_OBJECTpublic: TcpThread(int socketDescriptor,int command, QObject *parent = 0); void run(); QTcpSocket *sockfd; int socketDescriptor; int command;signals: void error(QTcpSocket::SocketError socketError); void displayInfo(int,QString, int); void updateBar(int, qint64);public slots: int processBuffer(QString ); void recvData();};#endif // TCPTHREAD_H//widget.h#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include "tcpthread.h"#include "tcpserver.h"class QDialogButtonBox;class QTcpSocket;namespace Ui { class Widget;}class Widget : public QWidget{ Q_OBJECTpublic: explicit Widget(QWidget *parent = 0); ~Widget(); TcpThread *thread ;private: Ui::Widget *ui; tcpserver tcpServer; QTime *currenttime;private slots: void on_pushButtonClearLog_clicked(); void on_pushButtonSaveLog_clicked(); void on_quitButton_clicked(); void on_pushButtonGET_clicked(); void on_OkButton_clicked(); void addNewRow(int); void displayInfo(int,QString, int); void updateBar(int, qint64);signals: void signal_send_command(int, int);};#endif // WIDGET_H//tcpserver.cpp#include "tcpserver.h"//构造函数tcpserver::tcpserver(QObject *parent) : QTcpServer(parent){}//重新定义了incomingConnection这个虚函数,//开辟一个新的tcpsocket线程,从TcpServer获得socketDescriptor,//并完成相应的信号连接void tcpserver::incomingConnection(int socketDescriptor){ qDebug() <<socketDescriptor; //clientDescriptorList.append(socketDescriptor); emit newRow(socketDescriptor); //display new client connect(this,SIGNAL(signal_send_command(int ,int)),this,SLOT(slot_send_command(int ,int)));}void tcpserver::slot_send_command(int sockDescriptor, int cmd){ TcpThread *thread = new TcpThread(sockDescriptor,cmd, this); thread->start(); connect(thread,SIGNAL(finished()),this,SLOT(deleteLater())); connect(thread,SIGNAL(displayInfo(int,QString,int)),this,SIGNAL(displayInfo(int,QString,int))); connect(thread,SIGNAL(updateBar(int,qint64)),this,SIGNAL(updateBar(int,qint64)));}//tcpthread.cpp#include "tcpthread.h"#include <QtGui>#include <QtNetwork>//构造函数完成简单的赋值/TcpThread::TcpThread(int socketDescriptor,int command, QObject *parent): QThread(parent),socketDescriptor(socketDescriptor),command(command){}void TcpThread::run(){ sockfd = new QTcpSocket; if (!sockfd->setSocketDescriptor(socketDescriptor)) { emit error(sockfd->error()); return; } sockfd -> write(GET); emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_GET); connect(sockfd, SIGNAL(readyRead()), this,SLOT(recvData()),Qt::DirectConnection); exec();}void TcpThread::recvData() //接收数据{ static qint64 bytesReceived = 0 ; //收到的总字节 static QString buffer = ""; //数据缓冲区 qint64 available = 0; if ( ( available = sockfd ->bytesAvailable() ) > 0 ) { bytesReceived += available; buffer.append(sockfd ->readAll()); //更新进度条 emit this->updateBar(socketDescriptor, bytesReceived); //display buffer emit this->displayInfo(socketDescriptor,buffer, DISPLAY_BUFFER); if ( processBuffer(buffer) == 1) { emit this->updateBar(socketDescriptor, 100); //更新进度条 goto GOTO_SEND_SUCCESS; } goto GOTO_SEND_AGAIN; GOTO_SEND_SUCCESS: //send SUCCESS sockfd -> write(SUCCESS); emit this-> displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_SUCCESS); return; GOTO_SEND_AGAIN: //send AGIAN emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SEND_AGIAN); if (-1 == sockfd -> write(AGAIN)) { qDebug() << "write error"; return; } }}int TcpThread::processBuffer(QString buffer){ static int END_FLAG_FINISH = 0 ; static int positionSTART = 0; static int positionFINISH = 0; static QHostAddress fileName ; static QFile *localFile ; static QString fileBuffer = ""; //file buffer END_FLAG_FINISH = 0; if (-1 != (positionSTART = buffer.indexOf(START)) ) //found START { if (-1 != (positionFINISH = buffer.indexOf(FINISH))) //found FINISH { emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), FOUND_FINISH); END_FLAG_FINISH = 1; fileBuffer = buffer.mid(positionSTART+36,positionFINISH -positionSTART - 36); fileName = sockfd->peerAddress(); quint16 port = sockfd->peerPort(); localFile = new QFile(fileName.toString()+(tr(".%1").arg(port))); //用户端的IP地址作为保存文件名 if(!localFile->open(QFile::WriteOnly)) { qDebug() << "open file error!"; return -1; } QByteArray filetemp = fileBuffer.toLatin1(); if ( -1 == ( localFile->write(filetemp.data(),qstrlen(filetemp.data())))) { qDebug() <<"write file error!"; } localFile->close(); fileBuffer.clear(); emit this->displayInfo(socketDescriptor,sockfd->peerAddress().toString(), SAVE_FILE_SUCCESS); return END_FLAG_FINISH; } } return END_FLAG_FINISH;}