前言

TCP传输控制协议 是一个可靠的(相对于UDP),面向流,面向连接的运输协议。
Socket 俗称“套接字”。就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
套接字Socket=(IP地址:端口号) QTcpSocket传输的过程是连续的(对于网络的要求是比较高的,稳定)。TCP编程一般分成客户端和服务器端,即C/S(Client/Server)架构。

实现部分–服务端

头文件实现

#ifndef MAINTCPSERVER_H
#define MAINTCPSERVER_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostInfo>

namespace Ui {
class MainTcpServer;
}

class MainTcpServer : public QMainWindow
{
Q_OBJECT

public:
explicit ExTcpServer(QWidget *parent = nullptr);
~ExTcpServer();

private:
QString getLocalIp(); //获取本机 IP

protected:
void closeEvent(QCloseEvent* event);

private slots:
//UI的槽函数
void on_actStart_triggered(); //开始监听
void on_actStop_triggered(); //停止监听
void on_actClear_triggered(); //清除文本框内容
void on_actQuit_triggered(); //退出程序
void on_btnSend_clicked(); //发送消息

//自定义的槽函数
void onSocketReadyRead(); //读取 socket 传入时候的数据
void onClientConnected(); //client socket conneted
void onClientDisonnected(); //client socket disconneted
void onNewConnection(); //QTcpServer 的 newConnect() 信号
void onSocketStateChange(QAbstractSocket::SocketState socketState);

private:
Ui::ExTcpServer *ui;

QLabel* m_labListen;
QLabel* m_labSocket;
QTcpServer* m_tcpServer;
QTcpSocket* m_tcpSocket;
};

#endif // MAINTCPSERVER_H

CPP 文件实现

#include "MainTcpServer.h"
#include "ui_MainTcpServer.h"

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

m_labListen = new QLabel("监听状态:");
m_labSocket = new QLabel("socket状态:");
m_labListen->setMidLineWidth(200);
m_labSocket->setMinimumWidth(200);
ui->statusBar->addWidget(m_labListen);
ui->statusBar->addWidget(m_labSocket);

QString localeIp = getLocalIp();
setWindowTitle(windowTitle() + "---IP地址:" + localeIp);
ui->comboBox->addItem(localeIp);

m_tcpServer = new QTcpServer(this);
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

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

QString MainTcpServer::getLocalIp()
{
QString hostName = QHostInfo::localHostName();
QHostInfo hostInfo = QHostInfo::fromName(hostName);
ui->plainTextEdit->appendPlainText("本机名称:" + hostName);
QString locaIp;

QList<QHostAddress> list = hostInfo.addresses();

if (list.empty())
return "null QString";

foreach (QHostAddress addr, list) {
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
locaIp = addr.toString();
break;
}
}

return locaIp;
}

void MainTcpServer::closeEvent(QCloseEvent *event) //关闭窗口时候停止监听
{
if (m_tcpServer->isListening())
m_tcpServer->close();

event->accept();
}

void MainTcpServer::on_actStart_triggered()
{
QString Ip = ui->comboBox->currentText();
quint16 port = ui->spinBox->value();

QHostAddress addr(Ip);
m_tcpServer->listen(addr, port); //监听指定的 IP 和指定的 port
ui->plainTextEdit->appendPlainText("服务器地址为:" + m_tcpServer->serverAddress().toString() + " 服务器端口:" + QString::number(m_tcpServer->serverPort()));
ui->plainTextEdit->appendPlainText("开始监听...");

ui->actStart->setEnabled(false);
ui->actStop->setEnabled(true);
m_labListen->setText("监听状态:正在监听...");
}

void MainTcpServer::on_actStop_triggered()
{
if (!m_tcpServer->isListening())
return;

m_tcpServer->close(); //停止监听

ui->actStart->setEnabled(true);
ui->actStop->setEnabled(false);
m_labListen->setText("监听状态:监听已经停止");
}

void MainTcpServer::on_actClear_triggered()
{
ui->plainTextEdit->clear();
}

void MainTcpServer::on_actQuit_triggered()
{
close();
}

void MainTcpServer::on_btnSend_clicked()
{
QString msg = ui->lineEdit->text();
ui->plainTextEdit->appendPlainText("[服务器:]" + msg);
ui->lineEdit->clear();
ui->plainTextEdit->hasFocus();

QByteArray str = msg.toUtf8();
str.append('\n');
m_tcpSocket->write(str);
}

void MainTcpServer::onSocketReadyRead() //读取缓冲区行文本
{
while (m_tcpSocket->canReadLine()) {
ui->plainTextEdit->appendPlainText("[客户端:]" + m_tcpSocket->readLine());
}
}

void MainTcpServer::onClientConnected() //客户端连接时
{
ui->plainTextEdit->appendPlainText("客户端套接字连接\n对等(peer)地址:" + m_tcpSocket->peerAddress().toString()
+ " 对等(peer)端口:" + QString::number(m_tcpSocket->peerPort()));

}

void MainTcpServer::onClientDisonnected() //客户端断开连接时
{
ui->plainTextEdit->appendPlainText("客户端套接字断开");
m_tcpSocket->deleteLater();
}

void MainTcpServer::onNewConnection()
{
m_tcpSocket = m_tcpServer->nextPendingConnection(); //创建 socket

connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(onClientConnected()));
connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(onClientDisonnected()));
connect(m_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(m_tcpSocket->state());
connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

void MainTcpServer::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
switch (socketState) {
case QAbstractSocket::UnconnectedState:
m_labSocket->setText("socket状态:UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
m_labSocket->setText("socket状态:HostLookupState");
break;
case QAbstractSocket::ConnectingState:
m_labSocket->setText("socket状态:ConnectingState");
break;
case QAbstractSocket::ConnectedState:
m_labSocket->setText("socket状态:ConnectedState");
break;
case QAbstractSocket::BoundState:
m_labSocket->setText("socket状态:BoundState");
break;
case QAbstractSocket::ClosingState:
m_labSocket->setText("socket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
m_labSocket->setText("socket状态:ListeningState");
break;
default:
m_labSocket->setText("socket状态:其他未知状态...");
break;
}
}

ui 文件实现

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExTcpServer</class>
<widget class="QMainWindow" name="ExTcpServer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>573</width>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string>ExTcpServer</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labIP">
<property name="text">
<string>监听地址:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labPort">
<property name="text">
<string>端口:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox">
<property name="maximum">
<number>65000</number>
</property>
<property name="value">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QPushButton" name="btnSend">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>573</width>
<height>25</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actStart"/>
<addaction name="actStop"/>
<addaction name="actClear"/>
<addaction name="actQuit"/>
<addaction name="separator"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actStart">
<property name="text">
<string>开始监听</string>
</property>
<property name="toolTip">
<string>开始监听</string>
</property>
</action>
<action name="actStop">
<property name="text">
<string>停止监听</string>
</property>
<property name="toolTip">
<string>停止监听</string>
</property>
</action>
<action name="actClear">
<property name="text">
<string>清除</string>
</property>
<property name="toolTip">
<string>清除文本</string>
</property>
</action>
<action name="actQuit">
<property name="text">
<string>退出</string>
</property>
<property name="toolTip">
<string>退出程序</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

实现界面如下:

TCP通信之QTcpServer和QTcpSocket,服务器和客户端通讯_Qt

实现部分–客户端

头文件实现

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QMainWindow>
#include <QLabel>
#include <QTcpSocket>
#include <QHostInfo>

namespace Ui {
class TcpClient;
}

class TcpClient : public QMainWindow
{
Q_OBJECT

public:
explicit TcpClient(QWidget *parent = nullptr);
~TcpClient();

private:
QString getLocalIp(); //获取本本机 IP

protected:
void closeEvent(QCloseEvent *event);

private slots:
//UI 定义的槽函数
void on_actConnect_triggered(); //请求连接到服务器
void on_actDisconnect_triggered(); //断开与服务器的连接
void on_actClear_triggered(); //清除内容
void on_actQuit_triggered(); //退出程序
void on_btnSend_clicked(); //发送文本消息

//自定义的槽函数
void onConnected();
void onDisconnected();
void onSocketReadyRead(); //从socket读取传入的数据
void onSocketStateChange(QAbstractSocket::SocketState socketState);

private:
Ui::TcpClient *ui;

QLabel* m_labSocket;
QTcpSocket* m_tcpSocket;
};

#endif // TCPCLIENT_H

cpp 文件实现

#include "TcpClient.h"
#include "ui_TcpClient.h"

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

m_labSocket = new QLabel("socket状态:");
m_labSocket->setMidLineWidth(150);
ui->statusBar->addWidget(m_labSocket);

m_tcpSocket = new QTcpSocket(this);

QString localIp = getLocalIp();
this->setWindowTitle(windowTitle() + "----本机IP:" + localIp);
ui->comboBox->addItem(localIp);

connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
connect(m_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}

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

QString TcpClient::getLocalIp()
{
QString hostName = QHostInfo::localHostName();
QHostInfo hostInfo = QHostInfo::fromName(hostName);
ui->plainTextEdit->appendPlainText("本机名称:" + hostName);
QString localIp;

foreach (QHostAddress addr, hostInfo.addresses()) {
if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
localIp = addr.toString();
break;
}
}
return localIp;
}

void TcpClient::closeEvent(QCloseEvent *event)
{
if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
m_tcpSocket->disconnectFromHost();

event->accept();
}

void TcpClient::onConnected()
{
ui->plainTextEdit->appendPlainText("已经连接到服务器\n客户端套接字连接\n对等(peer)地址:" + m_tcpSocket->peerAddress().toString()
+ " 对等(peer)端口:" + QString::number(m_tcpSocket->peerPort()));
ui->actConnect->setEnabled(false);
ui->actDisconnect->setEnabled(true);
}

void TcpClient::onDisconnected()
{
ui->plainTextEdit->appendPlainText("已经断开与服务器的连接\n");
ui->actConnect->setEnabled(true);
ui->actDisconnect->setEnabled(false);
}

void TcpClient::onSocketReadyRead()
{
while (m_tcpSocket->canReadLine()) {
ui->plainTextEdit->appendPlainText("[服务器:]" + m_tcpSocket->readLine());
}
}

void TcpClient::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
switch (socketState) {
case QAbstractSocket::UnconnectedState:
m_labSocket->setText("socket状态:UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
m_labSocket->setText("socket状态:HostLookupState");
break;
case QAbstractSocket::ConnectingState:
m_labSocket->setText("socket状态:ConnectingState");
break;
case QAbstractSocket::ConnectedState:
m_labSocket->setText("socket状态:ConnectedState");
break;
case QAbstractSocket::BoundState:
m_labSocket->setText("socket状态:BoundState");
break;
case QAbstractSocket::ClosingState:
m_labSocket->setText("socket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
m_labSocket->setText("socket状态:ListeningState");
break;
default:
m_labSocket->setText("socket状态:其他未知状态...");
break;
}
}

void TcpClient::on_actConnect_triggered()
{
QString addr = ui->comboBox->currentText();
quint16 port = ui->spinBox->value();
m_tcpSocket->connectToHost(addr, port);
}

void TcpClient::on_actDisconnect_triggered()
{
if(m_tcpSocket->state() == QAbstractSocket::ConnectedState)
m_tcpSocket->disconnectFromHost();
}

void TcpClient::on_actClear_triggered()
{
ui->plainTextEdit->clear();
}

void TcpClient::on_actQuit_triggered()
{
close();
}

void TcpClient::on_btnSend_clicked()
{
QString msg = ui->lineEdit->text();
ui->plainTextEdit->appendPlainText("[客户端:]" + msg);
ui->lineEdit->clear();
ui->lineEdit->setFocus();

QByteArray str = msg.toUtf8();
str.append('\n');
m_tcpSocket->write(str);
}

ui 设计文件

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExTcpClient</class>
<widget class="QMainWindow" name="ExTcpClient">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>561</width>
<height>353</height>
</rect>
</property>
<property name="windowTitle">
<string>ExTcpClient</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labIP">
<property name="text">
<string>服务器地址:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox">
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labPort">
<property name="text">
<string>端口:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox">
<property name="maximum">
<number>65000</number>
</property>
<property name="value">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QPushButton" name="btnSend">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>561</width>
<height>25</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actConnect"/>
<addaction name="actDisconnect"/>
<addaction name="actClear"/>
<addaction name="actQuit"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actConnect">
<property name="text">
<string>请求连接</string>
</property>
<property name="toolTip">
<string>请求连接到服务器</string>
</property>
</action>
<action name="actDisconnect">
<property name="text">
<string>断开连接</string>
</property>
<property name="toolTip">
<string>断开与服务器的连接</string>
</property>
</action>
<action name="actClear">
<property name="text">
<string>清空</string>
</property>
<property name="toolTip">
<string>清空编辑框文本</string>
</property>
</action>
<action name="actQuit">
<property name="text">
<string>退出</string>
</property>
<property name="toolTip">
<string>退出程序</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

ui界面如下:

TCP通信之QTcpServer和QTcpSocket,服务器和客户端通讯_服务器_02