Qt学习之TCP/IP网络编程

1 引言

测控系统中不同电脑和电脑之间需要用网络进行通信,基于TCP/IP协议下我们利用QTcpSocket套接字类和QTcpServer类设计服务器端和客户端的程序。

2 准备工作

在工程文件中添加

QT+=network

在头文件中添加

#include <QTcpServer>
#include <QTcpSocket>

3 客户端编程

(1)连接服务器
设计好客户端界面以后,定义一个套接字对象用于通信:

QTcpSocket *MyClientSocket;

连接服务器的代码如下:

MyClientSocket=new QTcpSocket();
connect(MyClientSocket,SIGNAL(connected()),this,SLOT(ConnectedFunc()));
connect(MyClientSocket,SIGNAL(disconnected()),this,SLOT(DisconnectedFunc()));
connect(MyClientSocket,SIGNAL(readyRead()),this,SLOT(ReadyReadFunc()));         
connect(MyClientSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(ErrorFunc());       
MyClientSocket->connectToHost(QHostInfo::localHostName(),9000);

调用connectToHost()函数连接服务器,函数参数为服务器端的主机名称和端口,在此之前,根据自己的需求将套接字对象的信号同自定义的槽函数连接,上述四个信号的含义是:如果成功连接会发出connected()信号,如果连接被服务器端断开会发出disconnected()信号,如果收到服务器端发来的数据,则会发出readyRead()函数,如果套接字发生错误,则发出error()信号,socketError参数描述了发生的错误类型。
(2)读取数据
在ReadyReadFunc()槽函数中使用read函数读取缓冲区中的数据,返回数据类型为QByteArray类型,参数为要读取的字节数,可以使用bytesAvailable()获取当前缓冲区中的所有字节数。

 buffer=MyClientSocket->read(MyClientSocket->bytesAvailable());

(3)发送数据
使用write函数发送数据,输入数据类型为QByteArray

MyClientSocket->write(buffer)

4 服务器端编程

服务器端需要写两个派生类,分别派生于QTcpServer类和QTcpSocket类,前者用于监听请求与服务器连接的套接字,后者与客户端套接字建立连接。
(1)监听套接字
派生于QTcpServer类简单的利用方法需要重写void incomingConnection(qintptr handle)事件处理函数,当有客户端连接时则会触发。派生类的头文件定义如下:

#ifndef MYSERVER_H
#define MYSERVER_H
#include <QObject>
#include <QTcpServer>
#include "myclientsocket.h"


class MyServer : public QTcpServer
{
    
    
    Q_OBJECT
public:
    MyServer();
    ~MyServer();
    MyClientSocket* client;
signals:
    void ConnectedToClient();

private:
    void incomingConnection(qintptr handle);
};

#endif // MYSERVER_H

这里的MyClientSocket类则是将要继承于QTcpSocket的类,该变量是与客户端建立连接的套接字。
打开服务器时,我们创建一个MyServer类的对象,使用listen()监听请求连接的客户端套接字,第一个参数为要监听的地址,第二个参数为要监听的端口,如果第一个参数为QHostAddress::Any,则表示将监听所有的网络接口,代码如下:

myserver=new MyServer;
if(!myserver->listen(QHostAddress::Any,9000)){
    
    
      ui->state->setText(QString("无法绑定端口"));
      return;
}

在重写的incomingConnection函数中,我们需要创建一个套接字对象,然后使用setSocketDescriptor(qintptr socketDescriptor)设置这个套接字对象的描述符,socketDescriptor参数是所接受连接的本机套接字描述符,设置以后,创建的套接字对象就会与客户端套接字建立连接了,代码如下。

void MyServer::incomingConnection(qintptr handle)
{
    
    
client=new MyClientSocket();
client->setSocketDescriptor(handle);
}

值得注意的是,如果有多个客户端要连接服务器,则需要创建创建多个套接字对象与其连接,总之,用于监听的套接字对象只需要一个,而用于通信的套接字对象与客户端数量有关,另外,如果你想在另一个线程中处理一个传入的连接作为一个新的QTcpSocket对象,你必须将socketDescriptor传递给另一个线程,并在那里创建QTcpSocket对象,并使用它的setSocketDescriptor()方法。
(2)通信套接字
与客户端套接字建立连接的套接字类继承于QTcpSocket类,头文件代码如下:

class MyClientSocket : public QTcpSocket
{
    
    
    Q_OBJECT
public:
    MyClientSocket();

private slots:
    void ReadData();
    void DisconnectedByClient();
};

#endif // MYCLIENTSOCKET_H

在这里我定义了ReadData()和DisconnectedByClient()两个槽函数和套接字的readyRead()信号和disconnected()连接,这两个信号的作用在上文中已经解释过了。在类的构造函数中将信号与槽连接,代码如下:

MyClientSocket::MyClientSocket()
{
    
    
    connect(this,SIGNAL(readyRead()),this,SLOT(ReadData()));
    connect(this,SIGNAL(disconnected()),this,SLOT(DisconnectedByClient()));
}

发送数据和接收数据依然使用write()函数与read()函数,用法就不再赘述了,值得注意的是,这里读取数据的槽函数定义在这个套接字新类中,我推荐这种做法,如果有多个客户端和服务器连接时,我们可以在类中增加一个int类型的ID用于标识客户端,同时我们在类中自定义一个信号,信号的参数为客户端ID,在对话框类定义一个对应的槽函数,当ReadData()收到并处理数据以后,发送这样一个带参数的信号给对话框,最后就可以在对话框的对应槽函数根据ID就知道是哪个连接的客户端发送过来数据了。

猜你喜欢

转载自blog.csdn.net/weixin_42411702/article/details/123618779