《QT_TCP下的socket编程.pdf》由会员分享,可在线阅读,更多相关《QT_TCP下的socket编程.pdf(9页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、QT TCP 下的 socket 编程转载:http:/ 转载 2011-03-01 17:06:41 阅读8 评论0 字号:大中小 订阅QTcpSocket 和 QTcpServer 类实现了Qt的Tcp 客户端和服务器。tcp 是一个流式协议。对于应用程序来说,数据是一个很长的流,有点像一个巨 大的文件。搞成此的协议建立在面向块的tcp 协议(Block-oriented)或面向行(Line-oriented)的tcp 协议上。面向块的tcp 协议,数据被当作一个2 进制的块来传输。没每一个块被当作一个 定义了大小的,后面跟随了数据的字段。面向行的tcp 协议,数据被当作一个文本文件的一行
2、。一个传输终止于一个新的 行的到来。QTcpSocket 继承自 QIODevice,所以它可以从 QDataStream 或 QTextStream 中读取或写入数据。“从文件读数据和从网络上读数据有一个明显的不同点:我们必须保证用 ”操作符读取数据时,已经从另一方接收了足够的数据。如果你这样做了,那么一 个失败的结果是:行为未定义。我们来看一个使用block-oriented tcp 协议的服务器和客户端的代码。用户填写行程的起始地,目的地,日期等,服务器返回符合要求的行程。界面用QDesigner“设计的。叫做 tripplanner.ui”。请使用uic 工具转换。include ui
3、_tripplanner.hclass TripPlanner:public QDialog,public Ui:TripPlanner Q_OBJECTpublic:TripPlanner(QWidget*parent=0);private slots:void connectToServer();void sendRequest();void updateTableWidget();void stopSearch();void connectionClosedByServer();void error();private:void closeConnection();QTcpSocket
4、tcpSocket;/tcpSocket 变量是QTcpSocket 类型,用来建立一个tcp 连接。quint16 nextBlockSize;/当需要提起从服务器传递来的数据块时,nextBlockSize 将被使用。;TripPlanner:TripPlanner(QWidget*parent):QDialog(parent)setupUi(this);QDateTime dateTime=QDateTime:currentDateTime();/取当前时间 dateEdit-setDate(dateTime.date();/将当前日期设置到相应控件中 timeEdit-setTime(
5、QTime(dateTime.time().hour(),0);progressBar-hide();/隐藏什么?progressBar-setSizePolicy(QSizePolicy:Preferred,QSizePolicy:Ignored);tableWidget-verticalHeader()-hide();/又隐藏?tableWidget-setEditTriggers(QAbstractItemView:NoEditTriggers);connect(searchButton,SIGNAL(clicked(),this,SLOT(connectToServer();/sear
6、ch 单击时,连接服务器 connect(stopButton,SIGNAL(clicked(),this,SLOT(stopSearch();/stop 停止连接服务器 connect(&tcpSocket,SIGNAL(connected(),this,SLOT(sendRequest();/如果连接好则发送返回信息?connect(&tcpSocket,SIGNAL(disconnected(),this,SLOT(connectionClosedByServer();/若没连接好,则关闭连接 connect(&tcpSocket,SIGNAL(readyRead(),this,SLOT
7、(updateTableWidget();connect(&tcpSocket,SIGNAL(error(QAbstractSocket:SocketError),this,SLOT(error();构造函数中,我们设置时间控件的默认属性,隐藏progressBar 等。连接tcpSocket 的connected(),disconnected(),readyRead(),error(QAbstractSocket:SocketError)信号到私有的槽。void TripPlanner:connectToServer()tcpSocket.connectToHost(tripserver.z
8、ugbahn.de,6178);tableWidget-setRowCount(0);searchButton-setEnabled(false);stopButton-setEnabled(true);statusLabel-setText(tr(Connecting to server.);progressBar-show();nextBlockSize=0;当用户点击searchButton 时,connectToServer()槽将被执行。它使用tcpSocket.connectToHost 建立到服务器的连接。connectToServer()槽立即返回。连接的动作实际发生在这之后。
9、当连接建立成功,QTcpSocket 触发connected()信号。如果失败,error()信号被触发。接着我们设置进度条以及按钮的状态。把nextBlockSize 设置为0.表示我们现在并不知道下一个接收的数据块的大小。void TripPlanner:sendRequest()QByteArray block;QDataStream out(&block,QIODevice:WriteOnly);out.setVersion(QDataStream:Qt_4_1);out quint16(0)quint8(S)currentText()currentText()date()time()
10、;if(departureRadioButton-isChecked()out quint8(D);else out seek(0);out setText(tr(Sending request.);当connected()信号被触发,sendRequest()槽被调用。sendRequest()向服务器发送一个请求(tcpSocket.write(block))。我们需要在数据块的第一个字段写入数据块的大小。但是当我们些第一个字段时,我们不知道整个数据块的大小,所以我们现写入0(out seek(0)),重新写入数据块的大小out rowCount();if(nextBlockSize=0)
11、if(tcpSocket.bytesAvailable()nextBlockSize;if(nextBlockSize=0 xFFFF)closeConnection();statusLabel-setText(tr(Found%1 trip(s).arg(row);break;if(tcpSocket.bytesAvailable()date departureTime duration changes trainType;arrivalTime=departureTime.addSecs(duration*60);tableWidget-setRowCount(row+1);QString
12、List fields;fields date.toString(Qt:LocalDate)departureTime.toString(tr(hh:mm)arrivalTime.toString(tr(hh:mm)tr(%1 hr%2 min).arg(duration/60).arg(duration%60)QString:number(changes)trainType;for(int i=0;i setItem(row,i,new QTableWidgetItem(fieldsi);nextBlockSize=0;当QTcpSocket 接收到数据时,readyRead()信号被触发。
13、updateTableWidget()槽 就被调用了。这里我们用了一个forever 循环,这是必须的!因为我们无法保证一次就接到了所有的数据块。可能,我们只接收到数据块的一个部分,也可能是全部。forever 循环是如何工作的呢?如果nextBlockSize 是0,表示我们没有独到 数据块的大小,我们必须重新读取它。数据块的大小字段必须至少读取sizeof(quint16)字节才能获得,如果读取的数据少于sizeof(quint16),必须重新读取。如果数据块大小字段为0 xFFFF,表示服务器端数据发送完毕,我们停止接收。最后我们设置nextBlockSize 为0,表示下一个数据块的大
14、小还不知道,我们必须接收。void TripPlanner:closeConnection()tcpSocket.close();searchButton-setEnabled(true);stopButton-setEnabled(false);progressBar-hide();当接收到的数据块大小字段的值为 0 xFFFF,我们关闭连接。void TripPlanner:stopSearch()statusLabel-setText(tr(Search stopped);closeConnection();如果stopServer 按钮被单击,我们关闭连接。void TripPlann
15、er:connectionClosedByServer()if(nextBlockSize!=0 xFFFF)statusLabel-setText(tr(Error:Connection closed by server);closeConnection();当服务器断开连接时,如果我们没有读到表示数据传送完毕的 0 xFFFF,我们发出一个错误。void TripPlanner:error()statusLabel-setText(tcpSocket.errorString();closeConnection();显示错误。主函数:int main(int argc,char*argv)Q
16、Application app(argc,argv);TripPlanner tripPlanner;tripPlanner.show();return app.exec();接下来,我们看看服务器端的实现。class TripServer:public QTcpServer Q_OBJECTpublic:TripServer(QObject*parent=0);private:void incomingConnection(int socketId);服务器端重新实现incomingConnection 方法。当客户端尝试连接到服务器的监听端口时,incomingConnection 方法被
17、触发。void TripServer:incomingConnection(int socketId)ClientSocket*socket=new ClientSocket(this);socket-setSocketDescriptor(socketId);class ClientSocket:public QTcpSocket Q_OBJECTpublic:ClientSocket(QObject*parent=0);private slots:void readClient();private:void generateRandomTrip(const QString&from,con
18、st QString&to,const QDate&date,const QTime&time);quint16 nextBlockSize;ClientSocket:ClientSocket(QObject*parent)ut quint16(0)).最后,当数据块填充完毕时,我们计算数据块的大小,将指针重新 :QTcpSocket(parent)connect(this,SIGNAL(readyRead(),this,SLOT(readClient();connect(this,SIGNAL(disconnected(),this,SLOT(deleteLater();nextBlockS
19、ize=0;void ClientSocket:readClient()QDataStream in(this);in.setVersion(QDataStream:Qt_4_1);if(nextBlockSize=0)if(bytesAvailable()nextBlockSize;if(bytesAvailable()requestType;if(requestType=S)in from to date time flag;srand(from.length()*3600+to.length()*60+time.hour();int numTrips=rand()%8;for(int i
20、=0;i numTrips;+i)ut quint16(0)).最后,当数据块填充完毕时,我们计算数据块的大小,将指针重新 generateRandomTrip(from,to,date,time);QDataStream out(this);out quint16(0 xFFFF);close();void ClientSocket:generateRandomTrip(const QString&/*from*/,const QString&/*to*/,const QDate&date,const QTime&time)QByteArray block;QDataStream out(&
21、block,QIODevice:WriteOnly);out.setVersion(QDataStream:Qt_4_1);quint16 duration=rand()%200;out quint16(0)date time duration quint8(1)seek(0);out quint16(block.size()-sizeof(quint16);write(block);int main(int argc,char*argv)QApplication app(argc,argv);TripServer server;if(!server.listen(QHostAddress:Any,6178)cerr Failed to bind to port endl;return 1;QPushButton quitButton(QObject:tr(&Quit);quitButton.setWindowTitle(QObject:tr(Trip Server);QObject:connect(&quitButton,SIGNAL(clicked(),&app,SLOT(quit();quitButton.show();return app.exec();
限制150内