Qt - TCP网络编程

网络编程

编写具有网络功能的程序就要用到Qt Network模块。该模块提供了一系列的接口用于TCP/IP编程。什么HTTP发送/接收请求啊、cookies相关的啊、DNS啊等都有对应的C++类可操作。使用network模块,需要在.pro文件中添加“QT += network”。

Qt5中所有网络相关的C++类的继承关系如下图:

1. QHostInfo

QHostInfo类为主机名查找提供了静态函数 。

QHostInfo查找与主机名关联的IP地址或与IP地址关联的主机名。 这个类提供了两个方便的静态函数:一个异步工作并在找到主机时发出信号,另一个阻塞并返回QHostInfo对象。

异步查找主机的IP地址,调用lookupHost(),它接受主机名或IP地址、接收方对象和槽签名作为参数并返回ID。 您可以通过使用查找ID调用abortHostLookup()来中止查找。

//通过域名查找ip
QHostInfo::lookupHost("www.baidu.com",this,[](const QHostInfo& info)
{
    qDebug()<<info.hostName()<<info.addresses();
});
//查找ip是否存在
QHostInfo::lookupHost("183.232.231.172",this,[](const QHostInfo& info)
{
    qDebug()<<info.hostName()<<info.addresses();
});

当结果准备好时,将调用该槽。 结果存储在QHostInfo对象中。 调用addresses()获取主机的IP地址列表,调用hostName()获取所查找的主机名。

如果查找失败,error()将返回所发生的错误类型。 errorString()给出了可读的查找错误描述。

如果你想要一个阻塞查找,使用QHostInfo::fromName()函数:

QHostInfo info = QHostInfo::fromName("smtp.qq.com");
qDebug()<<info.hostName()<<info.addresses();

QHostInfo通过IDNA和Punycode标准支持国际化域名(IDNs)。

要检索本地主机的名称,请使用静态QHostInfo::localHostName()函数。

qDebug()<< QHostInfo::localHostName();

 

2. QHostAddress

QHostAddress类提供一个IP地址。

这个类以独立于平台和协议的方式保存IPv4或IPv6地址。

QHostAddress通常与QTcpSocket、QTcpServer和QUdpSocket一起使用,以连接到主机或建立服务器。

主机地址用setAddress()设置,用toIPv4Address()、toIPv6Address()或toString()检索。 可以使用protocol()检查类型。

注意:请注意QHostAddress不做DNS查找。 QHostInfo是需要的。

这个类还支持常见的预定义地址:Null、LocalHost、LocalHostIPv6、Broadcast和Any。

枚举 描述
QHostAddress::Null 空地址对象。 相当于QHostAddress()。 参见QHostAddress: isNull()。
QHostAddress::LocalHost IPv4本地主机地址。 相当于QHostAddress(127.0.0.1)。
QHostAddress::localhsotIPv6 IPv6本地主机地址。 相当于QHostAddress(“::1”)。
QHostAddress::Broadcast IPv4广播地址。 相当于QHostAddress(“255.255.255.255”)
QHostAddress::AnyIPv4 IPv4任何地址。 相当于QHostAddress(“0.0.0.0”)。 与此地址绑定的套接字只在IPv4接口上监听。
QHostAddress::AnyIPv6 IPv6任何地址。 相当于QHostAddress(“::”)。 与此地址绑定的套接字只在IPv6接口上监听。
QHostAddress::Any 双栈任意地址。 与此地址绑定的套接字将侦听IPv4和IPv6接口。

 

3. QNetworkInterface

QNetworkInterface类提供了主机的IP地址和网络接口的列表。

QNetworkInterface表示一个连接到正在运行程序的主机的网络接口。 每个网络接口可以包含0个或多个IP地址,每个IP地址可选地与一个网络掩码和/或一个广播地址相关联。 这类三元组的列表可以通过addressEntries()获得。 或者,当网络掩码或广播地址或其他信息不需要时,使用方便的allAddresses()函数只获取活动接口的IP地址。

QNetworkInterface还使用hardwareAddress()报告接口的硬件地址。

并非所有操作系统都支持报告所有特性。 在所有平台中,只有IPv4地址保证被这个类列出。 其中IPv6地址列表仅支持在Windows、Linux、macOS和bsd等操作系统上使用。

  • 这个方便的函数返回主机上找到的所有IP地址。

QList<QHostAddress> addrlist = QNetworkInterface::allAddresses();
for(QHostAddress addr : addrlist)                                  
{                                                                  
    qDebug()<<addr.protocol()<<addr.toString();                    
}
  • 返回主机上找到的所有网络接口的列表。 如果失败,它将返回一个没有元素的列表。

QList<QNetworkInterface> networkList =  QNetworkInterface::allInterfaces();
for(auto inter : networkList)
{
    if(!inter.isValid())
        continue;
    //输出此网络接口的名称、接口的类型、MAC地址和 在Windows上返回这个网络接口的人类可读的名称(如以太网、本地连接等)
    qDebug()<<inter.name()<<inter.type()<<inter.hardwareAddress()<<inter.humanReadableName();
    //输出网络接口对应的ip地址
    for(auto entrys : inter.addressEntries())
    {
        qDebug()<<entrys.ip();
    }
}

 

4. QNetworkAddressEntry

QNetworkAddressEntry类存储一个由网络接口支持的IP地址,以及它相关的网络掩码和广播地址。

//返回IPv4地址和子掩码相关联的广播地址
QHostAddress broadcast() const
//返回网络接口中的IPv4或IPv6地址
QHostAddress ip() const
//返回与IP地址相关联的子网掩码
QHostAddress netmask() const

 

5. QAbstractSocket

QAbstractSocket类提供了所有套接字类型通用的基本功能 。

QAbstractSocket是QTcpSocket和QUdpSocket的基类,包含这两个类的所有通用功能。 如果你需要一个套接字,你有两个选择:

  • 实例化QTcpSocket或QUdpSocket。

  • 创建本机套接字描述符,实例化QAbstractSocket,并调用setSocketDescriptor()来包装本机套接字。

TCP(传输控制协议)是一种可靠的、面向流的、面向连接的传输协议。 UDP(用户数据报协议)是一个不可靠的、面向数据报的、无连接的协议。 在实践中,这意味着TCP更适合于数据的连续传输,而更轻量级的UDP可以在可靠性不重要的情况下使用。

QAbstractSocket的API统一了这两种协议之间的大部分差异。 例如,尽管UDP是无连接的,但connectToHost()为UDP套接字建立了一个虚拟连接,使您能够以几乎相同的方式使用QAbstractSocket,而不管底层协议是什么。 在内部,QAbstractSocket记住传递给connectToHost()的地址和端口,read()和write()等函数使用这些值。

在任何时候,QAbstractSocket都有一个状态(由state()返回)。 初始状态为UnconnectedState。 调用connectToHost()后,套接字首先进入HostLookupState(socket正在查找主机名)。 如果找到主机,QAbstractSocket进入ConnectingState并发出hostFound()信号。 当连接建立后,它进入ConnectedState并发出connected()。 如果在任何阶段发生错误,则会触发error()。 每当状态改变时,就会触发stateChanged()。 为了方便起见,如果套接字已经准备好读写,isValid()将返回true,但是请注意,在读写发生之前,套接字的状态必须是ConnectedState。

通过调用Read()或write()读取或写入数据,或使用方便的函数readLine()和readAll()。 QAbstractSocket还从QIODevice继承了getChar()、putChar()和ungetChar(),它们处理单个字节。 当数据被写入套接字时将发出bytesWritten()信号。 注意,Qt不限制写缓冲区的大小。 你可以通过听这个信号来监控它的大小。

readyRead()信号在每次到达一个新的数据块时被触发。 然后bytesAvailable()返回可用于读取的字节数。 通常,您将readyRead()信号连接到一个插槽并读取那里的所有可用数据。 如果您没有一次读取所有数据,其余的数据将在稍后仍然可用,并且任何新的传入数据将被附加到QAbstractSocket的内部读缓冲区。 要限制读缓冲区的大小,可以调用setReadBufferSize()。

要关闭套接字,调用disconnectFromHost()。 QAbstractSocket进入QAbstractSocket:: ClosingState。 在将所有挂起的数据写入套接字之后,QAbstractSocket实际上关闭了套接字,输入QAbstractSocket::UnconnectedState,并发出disconnected()。 如果您想立即中止连接,丢弃所有挂起的数据,可以调用abort()。 如果远程主机关闭连接,QAbstractSocket将发出错误(QAbstractSocket::RemoteHostClosedError),在此期间,套接字状态仍然是ConnectedState,然后将发出disconnected()信号。

通过调用 peerPort() 和 peerAddress() 获取连接的对等方的端口和地址。 peerName() 返回传递给 connectToHost() 的对等方的主机名。 localPort() 和 localAddress() 返回本地套接字的端口和地址。

QAbstractSocket提供了一组函数,用于挂起调用线程,直到发出某些信号。 这些函数可以用来实现阻塞套接字:

  • waitForConnected()将阻塞,直到建立连接。

  • waitForReadyRead()会阻塞,直到有新的数据可以读取。

  • waitForBytesWritten()将阻塞,直到一个数据负载被写入套接字。

  • waitForDisconnected()将阻塞,直到连接关闭。

下面是一段示例:

int numRead = 0, numReadTotal = 0;
char buffer[50];
​
forever 
{
     numRead  = socket.read(buffer, 50);
     // do whatever with array
     numReadTotal += numRead;
     if (numRead == 0 && !socket.waitForReadyRead())
         break;
}

如果waitForReadyRead()返回false,则表示连接已经关闭或发生了错误。

使用阻塞套接字编程与使用非阻塞套接字编程完全不同。 阻塞套接字不需要事件循环,通常会导致更简单的代码。 然而,在GUI应用程序中,阻塞套接字应该只在非GUI线程中使用,以避免冻结用户界面。 请参阅fortuneclient和blockingfortuneclient示例来了解这两种方法的概述。

注意:我们不建议将阻塞函数与信号一起使用。 应该使用两种可能性中的一种。

QAbstractSocket可以与QTextStream和QDataStream的流操作符(操作符<<()和操作符>>())一起使用。 但是有一个问题需要注意:在尝试使用操作符>>()读取数据之前,必须确保有足够的数据可用。

如果想要了解更多信息,可以查阅QNetworkAccessManager和QTcpServer。

QAbstractSocket类继承自 : QIODevice.

 

5.1 信号

void connected()//这个信号是在调用connectToHost()并成功建立连接之后发出的。
void disconnected()//当套接字断开连接时,会发出此信号。
void error(QAbstractSocket::SocketError socketError)//这个信号是在发生错误后发出的。socketError参数描述发生的错误类型。
void hostFound()//这个信号是在调用connectToHost()并且主机查找成功之后发出的。
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)//当使用需要身份验证的代理时,可以发出此信号。然后可以用所需的详细信息填充身份验证者对象,以允许身份验证并继续连接。
void stateChanged(QAbstractSocket::SocketState socketState)//每当QAbstractSocket的状态发生变化时,就会发出这个信号。socketState参数是新的状态。

•从QIODevice继承4个信号

void aboutToClose()//这个信号是在设备即将关闭时发出的。如果你有需要在设备关闭之前执行的操作,连接这个信号(例如,如果你有数据在一个单独的缓冲区,需要写入设备)。
void bytesWritten(qint64 bytes)//每当向设备写入有效数据时,就会发出这个信号。bytes参数设置为写入此有效负载的字节数。
void readChannelFinished()//当该设备中的输入(读取)流关闭时,该信号被发出。一旦检测到关闭,就会触发该事件,这意味着可能仍然有数据可用read()进行读取。
void readyRead()//每当从设备读取新数据时,该信号就会发出一次。只有当有新的数据可用时,它才会再次发出,例如当网络数据的新负载到达您的网络套接字时,或者当一个新的数据块被附加到您的设备时。

•从QObject继承了2个信号

void destroyed(QObject *obj = Q_NULLPTR)//该信号在对象obj被销毁之前立即发出,并且不能被阻塞。所有对象的子对象在信号发出后立即被销毁。
void objectNameChanged(const QString &objectName)//该信号在对象的名称被更改之后发出。新的对象名称作为objectName传递。注意:这是私人信号。它可以用于信号连接,但不能由用户发出。注意:属性objectName的通知符信号。参见QObject::的objectName。

 

5.2 公有函数

函数 描述
void abort() 终止当前连接并重置套接字。与disconnectFromHost()不同,这个函数会立即关闭套接字,丢弃写缓冲区中任何挂起的数据。
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform) 使用BindMode模式绑定到端口上的地址。
bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform) 这是一个重载函数。绑定到QHostAddress:任何端口端口,使用BindMode模式。
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol) 尝试连接到给定端口上的hostName。protocol参数可用于指定使用哪种网络协议(ea. IPv4或IPv6)。
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite) 这是一个重载函数。尝试连接到端口上的地址。
virtual void disconnectFromHost() 试图关闭套接字。如果有挂起的数据等待写入,QAbstractSocket将进入ClosingState并等待,直到所有数据都已写入。最后,它将进入UnconnectedState并发出disconnected()信号。
SocketError error() const 返回上次发生的错误类型。
bool flush() 该函数尽可能多地从内部写缓冲区写入底层网络套接字,而不会阻塞。如果写入了任何数据,该函数返回true;否则返回false。
bool isValid() const 如果套接字有效并可以使用,则返回true;否则返回false。
QHostAddress localAddress() const 如果可用,返回本地套接字的主机地址;否则返回QHostAddress::零。
quint16 localPort() const 如果可用,返回本地套接字的主机端口号(以本机字节顺序);否则返回啊。
PauseModes pauseMode() const 返回此套接字的暂停模式。
QHostAddress peerAddress() const 如果套接字处于ConnectedState状态,返回连接的对等体的地址;否则返回QHostAddress::零。
QString peerName() const 返回connectToHost()指定的对等体名称,如果connectToHost()没有被调用,则返回一个空QString。
quint16 peerPort() const 如果套接字在ConnectedState中,返回连接的对等体的端口;否则返回0。
QNetworkProxy proxy() const 返回此套接字的网络代理。默认情况下使用QNetworkProxy::DefaultProxy,这意味着这个套接字将查询应用程序的默认代理设置。
qint64 readBufferSize() const 返回内部读缓冲区的大小。这限制了在调用read()或readAll()之前客户端可以接收的数据量。读取缓冲区大小为O(默认值)意味着缓冲区没有大小限制,确保没有数据丢失。
virtual void resume() 在套接字上继续数据传输。此方法仅应在套接字被设置为暂停通知并接收到通知之后使用。目前唯一支持的通知是QSslSocket::sslErrors()。如果套接字没有暂停,调用此方法会导致未定义的行为。
void setPauseMode(PauseModes pauseMode) 控制在收到通知时是否暂停。pauseMode参数指定套接字应该暂停的条件。目前唯一支持的通知是QSslSocket::sslErrors()。如果设置为PauseOnSslErrors,套接字上的数据传输将暂停,需要通过调用resume()再次显式启用。默认情况下,该选项设置为PauseNever。在连接到服务器之前必须调用此选项,否则将导致未定义的行为。
void setProxy(const QNetworkProxy &networkProxy) 将此套接字的显式网络代理设置为networkProxy。要禁用这个套接字的代理,使用QNetworkProxy::NoProxy代理类型:
virtual void setReadBufferSize(qint64 size) 将QAbstractSocket的内部读取缓冲区的大小设置为大小字节。
virtual bool setSocketDescriptor(qintptr socketDescriptor, SocketState socketState = ConnectedState, OpenMode openMode = ReadWrite) 使用本机套接字描述符socketDescriptor初始化QAbstractSocket。如果socketDescriptor被接受为有效的套接字描述符,则返回true;否则返回false。socket以openMode指定的模式打开,并进入socketState指定的socket状态。读取和写入缓冲区将被清除,丢弃任何挂起的数据。
virtual void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) 将给定选项设置为由value描述的值。注意:在Windows运行时上,在连接套接字之前必须设置QAbstractSocket::KeepAliveOption。
virtual qintptr socketDescriptor() const 如果可用,则返回QAbstractSocket对象的本机套接字描述符;否则返回1。
virtual QVariant socketOption(QAbstractSocket::SocketOption option) 返回选项的值。
SocketType socketType() const 返回套接字类型(TCP, UDP或其他)。
SocketState state() const 返回套接字的状态。
virtual bool waitForConnected(int msecs = 30000) 等待直到套接字被连接,最长可达msecs毫秒。如果连接已经建立,这个函数返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。
virtual bool waitForDisconnected(int msecs = 30000) 等待直到套接字断开连接,最长可达msecs毫秒。如果连接已经断开,这个函数返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。

 

6. QTcpServer(监听套接字)

QTcpServer类提供了一个基于tcp服务器的监听套接字。

这个类使接收传入的TCP连接成为可能。 您可以指定端口或让QTcpServer自动选择一个端口。 您可以监听一个特定的地址或所有机器的地址。

调用listen()让服务器监听传入的连接。 然后,每当客户机连接到服务器时,就会发出newConnection()信号。

调用nextPendingConnection()接受挂起的连接作为已连接的QTcpSocket。 该函数返回一个指向QAbstractSocket::ConnectedState中的QTcpSocket的指针,您可以使用该指针与客户端通信。

如果发生了错误,serverError()返回错误的类型,可以调用errorString()来获得人们可读的关于发生了什么事情的描述。

当监听连接时,服务器监听的地址和端口可用serverAddress()和serverPort()获取。

调用close()会使QTcpServer停止监听传入的连接。

虽然QTcpServer主要是为使用事件循环而设计的,但也可以不使用事件循环。 在这种情况下,必须使用waitForNewConnection(),它会阻塞直到连接可用或超时过期。

 

QTcpServer类继承自: QObject

6.1 信号

void acceptError(QAbstractSocket::SocketError socketError)//当接受新连接导致错误时,会发出此信号。
void newConnection()//每当有新的连接可用时,就会发出这个信号。

 

6.2 公有函数

函数 描述
void close() 关闭服务器。服务器将不再监听传入的连接。
QString errorString() const 返回最近发生的错误的人类可读的描述。
virtual bool hasPendingConnections() const 如果服务器有一个挂起的连接,返回true;否则返回
bool isListening() const 如果服务器当前正在监听传入的连接,则返回true;否则返回false。
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) 服务器开始监听指定addres和port上的连接。
int maxPendingConnections() const 返回挂起的已接受连接的最大数目。默认值为30。
virtual QTcpSocket *nextPendingConnection() 返回下一个挂起的连接作为已连接的QTcpSocket对象。
void pauseAccepting() 暂停接受新连接。排队连接将保持在队列中。
QNetworkProxy proxy() const 返回此套接字的网络代理。默认使用QNetworkProxy::DefaultProxy。
void resumeAccepting() 恢复接受新连接。
QHostAddress serverAddress() const 如果服务器正在监听连接,返回服务器地址;否则返回QHostAddress::零。
QAbstractSocket::SocketError serverError() const 返回发生的最后一个错误的错误码。
quint16 serverPort() const 如果服务器正在监听连接,返回服务器的端口;否则返回0。
void setMaxPendingConnections(int numConnections) 将挂起的接受连接的最大数量设置为numConnections。在调用nextPendingConnection()之前,QTcpServer将接受不超过numConnections的传入连接。默认情况下,这个限制是30个挂起的连接。
void setProxy(const QNetworkProxy &networkProxy) 将此套接字的显式网络代理设置为networkProxy。
bool setSocketDescriptor(qintptr socketDescriptor) 设置此服务器在监听到socketDescriptor的传入连接时应该使用的套接字描述符。如果套接字设置成功,返回true;否则返回false。
qintptr socketDescriptor() const 设置此服务器在监听到socketDescriptor的传入连接时应该使用的套接字描述符。如果套接字设置成功,返回true;否则返回false。假定套接字处于监听状态。
bool waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR) 等待最多毫秒或直到传入连接可用。如果连接可用,返回true;否则返回false。如果操作超时且timeout不是O,则* timeout将被设为true。

•从QObject继承了31个公共函数,具体查看Qt帮助文档

 

7. QTcpSocket(通信套接字)

TCP(Transmission Control Protocol,传输控制协议)

TCP是一个用于数据传输的地城网络协议,多个网络协议包括(HTTP和FTP都是基于TCP协议),TCP是面向数据流和面向连接的可靠的传输协议。

QTcpSocket类继承自QAbstractSocket,与QUdpSocket传输的数据报不同的是,QTcpSocket传输的是连续的数据流,尤其适合连续的数据传输,TCP一般分为客户端和服务端,即C/S (Client/Server模型)。

QTcpSocket代表了两个独立的数据流,一个用来读取数据,一个用来写入数据,分别采用QTcpSocket::read()及QTcpSocket::write()操作,读取数据前先调用QTcpSocket::bytesAvailable来确定已有足够的数据可用。

QTcpServer处理客户端的连接,可通过QTcpServer::listen()监听客户端发来的连接请求,每当有客户端连接时会发射newConnection()信号,QTcpSocket可用于读取客户端发来的数

 

7.1 信号

 

7.2 公有函数

 

•从QAbstractSocket继承了37个公共函数,上面有写QAbstractSocket的公有函数

•从QIODevice继承了33个公共函数,具体不详细描述,常用的有以下函数:

qint64 QIODevice::read(char *data, qint64 maxSize)//从设备读取最多maxSize字节到数据,并返回读取的字节数。如果发生错误,例如试图读取以WriteOnly模式打开的设备时,此函数返回-1。
    
QByteArray QIODevice::read(qint64 maxSize)//这是一个重载函数。从设备读取最多maxSize字节,并将读取的数据作为QByteArray返回。这个函数没有方法报告错误;返回一个空的QByteArray可能意味着当前没有数据可供读取,或者发生了错误。
    
QByteArray QIODevice::readAll()//从设备读取所有剩余数据,并以字节数组的形式返回。这个函数没有方法报告错误;返回一个空的QByteArray可能意味着当前没有数据可供读取,或者发生了错误。
    
qint64 QIODevice::write(const char *data, qint64 maxSize)//将数据写入设备的数据最多为maxSize字节。返回实际写入的字节数,如果发生错误则返回-1。
    
qint64 QIODevice::write(const char *data)//这是一个重载函数。将以零结尾的8位字符字符串写入设备。返回实际写入的字节数,如果发生错误则返回-1。
    
qint64 QIODevice::write(const QByteArray &byteArray)//这是一个重载函数。将byteArray的内容写入设备。返回实际写入的字节数,如果发生错误则返回-1。

•从QObject继承了31个公共函数,具体查看Qt帮助文档

 

8. TCP聊天程序

Qt Tcp通信和windows的类似,分服务端和客户端,模型如下

服务器通信流程:

  • 1.创建QTcpServer对象
  • 2.启动服务器(监听)调用成员方法listen(QHostAddress::Any,端口号)
  • 3.当有客户端链接时候会发送newConnection信号,触发槽函数接受链接(得到一个与客户端通信的套接字QTcpSocket)
  • 4.QTcpsocket发送数据用成员方法write,
  • 5.读数据当客户端有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据

客户端通信流程 :

  • 1.创建QTcpSocket对象
  • 2.链接服务器connectToHost(QHostAddress("ip"),端口号)
  • 3.QTcpsocket发送数据用成员方法write,
  • 4.读数据当对方有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据

 

文件结构:

 

8.1 TCP服务器

widget.h代码

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>   //监听套接字
#include <QTcpSocket>   //通信套接字

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void slotsNewConnection();//有新的客户端来连接
    void slotsReadyRead();//读取客户端发来的数据
    void slotsDisconnected();//客户端断开连接
    void on_btnSend_clicked();
    void on_benClose_clicked();

private:
    Ui::Widget *ui;
    QTcpServer* tcpServer;//监听套接字
    QTcpSocket* tcpSocket;//通信套接字
};
#endif // WIDGET_H

 

widget.cpp代码

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("Server");
    ui->textEditRead->setReadOnly(true);

    tcpServer = nullptr;
    tcpSocket = nullptr;
    tcpServer = new QTcpServer(this);//初始化服务器server对象

    //启动服务器监听
    tcpServer->listen(QHostAddress::Any, 8888);
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(slotsNewConnection())); //有新的客户端连接
}

Widget::~Widget()
{
    delete ui;
}

void Widget::slotsNewConnection()
{
    qDebug()<<"有新客户端连接";

    //接受客户端连接,并取出建立好连接的客户端套接字
    tcpSocket = tcpServer->nextPendingConnection();
    //获取对方的IP和端口
    QString ip = tcpSocket->peerAddress().toString();
    qint16 port = tcpSocket->peerPort();
    QString temp = QString("[%1 : %2]: 成功连接").arg(ip).arg(port);
    ui->textEditRead->setText(temp);

    //关联接收客户端数据信号readyRead信号(客户端有数据就会发readyRead信号)
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(slotsReadyRead()));
    //检测掉线信号,客户端断开连接
    connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(slotsDisconnected()));
}

void Widget::slotsReadyRead()
{
    //从通信套接字中取出内容
    QByteArray array = tcpSocket->readAll();
    ui->textEditRead->append(array);
}

void Widget::slotsDisconnected()
{
    QTcpSocket *obj = (QTcpSocket*)sender();//掉线对象
    QString ip = obj->peerAddress().toString();
    quint16 port = obj->peerPort();

    QString temp = QString("客户端:%1 %2断开了连接!").arg(ip).arg(port);
    qDebug()<<temp;//打印出掉线对象的ip
    ui->textEditRead->append(temp);
}

void Widget::on_btnSend_clicked()
{
    if(tcpSocket == nullptr)
    {
        return;
    }

    //获取编辑区内容
    QString str = ui->textEditWrite->toPlainText();

    //给对方发送数据,使用套接字是tcpSocket
    tcpSocket->write(str.toUtf8().data());

    //清空编辑区内容
    ui->textEditWrite->clear();
}

void Widget::on_benClose_clicked()
{
    if(tcpSocket == nullptr)
    {
        return;
    }

    //主动和对方断开连接
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
    tcpSocket = nullptr;
}

 

8.2 TCP客户端

widget.h代码

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void slotsConnected();//成功和服务器建立好连接
    void slotsReadyRead();//读取服务器发来的数据
    void on_btnConnect_clicked();
    void on_btnSend_clicked();
    void on_btnClose_clicked();


private:
    Ui::Widget *ui;

    QTcpSocket* tcpSocket;
};
#endif // WIDGET_H

widget.cpp代码

#include "widget.h"
#include "ui_widget.h"

#include <QHostAddress>

Widget::Widget(QWidget *parent): QWidget(parent) , ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("Client");
    ui->textEditRead->setReadOnly(true);

    tcpSocket = nullptr;
    tcpSocket = new QTcpSocket(this);

    connect(tcpSocket,SIGNAL(connected()),this,SLOT(slotsConnected()));
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(slotsReadyRead()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::slotsConnected()
{
    ui->textEditRead->setText("成功和服务器建立好连接!");
}

void Widget::slotsReadyRead()
{
    //从通信套接字中取出内容
    QByteArray array = tcpSocket->readAll();
    ui->textEditRead->append(array);
}

void Widget::on_btnConnect_clicked()
{
    //获取服务器Ip和端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //主动和服务器建立连接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}

void Widget::on_btnSend_clicked()
{
    //获取编辑区内容
    QString str = ui->textEditWrite->toPlainText();

    //给对方发送数据,使用套接字是tcpSocket
    tcpSocket->write(str.toUtf8().data());

    //清空编辑区内容
    ui->textEditWrite->clear();
}

void Widget::on_btnClose_clicked()
{
    //主动和对方断开连接
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
}

 

posted @ 2022-07-17 09:40  [BORUTO]  阅读(699)  评论(0编辑  收藏  举报