C# · 12月 20, 2021

QT(C++)实现网络聊天程序的设计与实现(基于TCP协议)

目录

        TCP/IP协议介绍

        网络聊天程序的功能介绍

        QT中socket编程

                ·服务器实现

                ·客户端实现

        测试结果

TCP/IP协议介绍

        TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件、软件需求的定义。TCP/IP协议确切的说法应该是TCP/UDP/IP协议。UDP协议(User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输。TCP协议(Transmission Control Protocol 传输控制协议),是一种流传输的协议。他提供可靠的、有序的、双向的、面向连接的传输。

        保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。

        而面向流则是指无保护消息边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。

        举例来说,假如,我们连续发送三个数据包,大小分别是2k、4k、8k,这三个数据包都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完。而使用TCP协议,我们只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。

        这就是因为UDP协议的保护消息边界使得每一个消息都是独立的。而流传输,却把数据当作一串数据流,它不认为数据是一个一个的消息。所以有很多人在使用TCP协议通讯的时候,并不清楚TCP是基于流的传输,当连续发送数据的时候,他们时常会认为TCP会丢包。其实不然,因为当它们使用的缓冲区足够大时,它们有可能会一次接收到两个甚至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个数据包,而已经接收的其它据包却被忽略了。

网络聊天程序的功能介绍

        对于此程序的功能和界面在这进行一个简单的说明。首先服务器功能包括打开服务器(监听IP和端口)、接收显示客户端发送过来的消息、与客户端进行通信、在没有客户端接入的时候通过连接信号槽可以的得知且发送按钮失效。其次服务器功能包括通过输入IP地址和端口号进行连接服务器、接受显示服务器发送过来的消息、与服务器进行通信、同样在没有成功连接服务器的时候通过连接信号槽可以的得知且发送按钮失效。

        此程序是一对一通信,如果需要进行多客户端通信的话,只需要多开几个线程进行监听即可。

QT中socket编程

        ·服务器实现

        服务器端编程的步骤:

        1:创建QT封装类TCP服务器对象server = new QTcpServer();

        2:使用server对象调用函数listen()绑定监听IP地址和端口号;

        3:建立连接信号槽connect()是否有客户端连接,有且获取客户端信息比如端口号;

        4:请求到来后,接受连接请求,socket对象可以与客户端进行通信;

        5:返回,等待另一连接请求;

        7:关闭server和socket。

        mainwindow.ui

<ui version="4.0">

MainWindow

<widget class="QMainWindow" name="MainWindow">

<property name="geometry">

0

0

550

583

<property name="windowTitle">

服务器

<widget class="QWidget" name="centralWidget">

<widget class="QLabel" name="label">

<property name="geometry">

110

30

72

31

<property name="text">

端口号:

<widget class="QLineEdit" name="lineEdit_Port">

<property name="geometry">

190

30

113

31

<widget class="QPushButton" name="pushButton_Listen">

<property name="geometry">

340

30

101

31

<property name="text">

OpenServer

<widget class="QTextEdit" name="textEdit_Send">

<property name="geometry">

40

360

451

101

<widget class="QTextEdit" name="textEdit_Recv">

<property name="geometry">

40

130

451

151

<widget class="QPushButton" name="pushButton_Send">

<property name="geometry">

400

480

93

28

<property name="text">

发送

<widget class="QLabel" name="label_2">

<property name="geometry">

40

80

91

41

<property name="font">

@H_273_404@

Agency FB

12

<property name="text">

消息内容:

<widget class="QLabel" name="label_3">

<property name="geometry">

40

310

221

41

<property name="font">

@H_273_404@

Agency FB

12

<property name="text">

服务器返回消息编辑:

<widget class="QMenuBar" name="menuBar">

<property name="geometry">

0

0

550

26

<widget class="QStatusBar" name="statusBar"/>

<layoutdefault spacing="6" margin="11"/>

           mainwindow.h

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include

#include

#include

namespace Ui {

class MainWindow;

}

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0);

~MainWindow();

private slots:

void on_pushButton_Listen_clicked();

void on_pushButton_Send_clicked();

void server_New_Connect();

void socket_Read_Data();

void socket_Disconnected();

private:

Ui::MainWindow *ui;

QTcpServer* server;

QTcpSocket* socket;

};

#endif // MAINWINDOW_H

        main.cpp

#include “mainwindow.h”

#include

int main(int argc,char *argv[])

{

QApplication a(argc,argv);

MainWindow w;

w.show();

return a.exec();

}

        mainwindow.cpp

#include “mainwindow.h”

#include “ui_mainwindow.h”

#include “QMessage@R_922_2419@”

#include “QTextCodec.h”

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent),

ui(new Ui::MainWindow)

{

ui->setupUi(this);

setFixedSize(550,583); // 禁止改变窗口大小。

ui->textEdit_Recv->setReadOnly(true);

ui->lineEdit_Port->setText(“6000”);

ui->pushButton_Send->setEnabled(false);

server = new QTcpServer();

//连接信号槽

connect(server,&QTcpServer::newConnection,this,&MainWindow::server_New_Connect);

}

MainWindow::~MainWindow()

{

server->close();

server->deleteLater();

delete ui;

}

void MainWindow::on_pushButton_Listen_clicked()

{

ui->textEdit_Recv->setText(“”);

if(ui->pushButton_Listen->text() == tr(“OpenServer”))

{

server = new QTcpServer();

//连接信号槽

connect(server,&MainWindow::server_New_Connect);

ui->textEdit_Recv->insertPlainText(“OpenSuccessfullyn”);

//从输入框获取端口号

int port = ui->lineEdit_Port->text().toInt();

//监听指定的端口

if(!server->listen(QHostAddress::Any,port))

{

//若出错,则输出错误信息

QTextCodec::setCodecForLocale(QTextCodec::codecForName(“GB18030”));

QMessage@R_922_2419@::information(this,tr(“消息”),tr(“打开服务器失败!”),QMessage@R_922_2419@::Yes);

return;

}

//修改按键文字

ui->pushButton_Listen->setText(“CloseServer”);

QTextCodec::setCodecForLocale(QTextCodec::codecForName(“GB18030”));

QMessage@R_922_2419@::information(this,tr(“打开服务器成功!”),QMessage@R_922_2419@::Yes);

}

else

{

server->deleteLater();

//取消侦听

server->close();

//修改按键文字

ui->pushButton_Listen->setText(“OpenServer”);

//发送按键失能

ui->pushButton_Send->setEnabled(false);

ui->textEdit_Recv->insertPlainText(“CloseSuccessfullyn”);

}

}

void MainWindow::on_pushButton_Send_clicked()

{

ui->textEdit_Recv->insertPlainText(tr(“服务器:”)+ui->textEdit_Send->toPlainText()+”n”);

socket->write(ui->textEdit_Send->toPlainText().toLocal8Bit());

socket->flush();

ui->textEdit_Send->setText(“”);

}

void MainWindow::server_New_Connect()

{

//获取客户端连接

socket = server->nextPendingConnection();

//连接QTcpSocket的信号槽,以读取新数据

QObject::connect(socket,&QTcpSocket::readyRead,&MainWindow::socket_Read_Data);

QObject::connect(socket,&QTcpSocket::disconnected,&MainWindow::socket_Disconnected);

//发送按键使能

ui->pushButton_Send->setEnabled(true);

}

void MainWindow::socket_Read_Data()

{

QByteArray buffer;

//读取缓冲区数据

buffer = socket->readAll();

if(!buffer.isEmpty())

{

QString str = QString::fromLocal8Bit(buffer);

ui->textEdit_Recv->insertPlainText(“李青说:”+str+”n”);

}

}

void MainWindow::socket_Disconnected()

{

//发送按键失能

ui->pushButton_Send->setEnabled(false);

}

        ·客户端实现

        客户端编程的步骤:

        1:创建QT封装TCP套接字对象socket=new QTcpSocket();

        2:建立连接信号槽connect()是否与服务器有连接;

        3:向服务器发出连接请求( socket->connectToHost(IP,port));

        4:和服务器端进行通信( socket->write());

        5:关闭套接字。

        mainwindow.ui

<ui version="4.0">

MainWindow

<widget class="QMainWindow" name="MainWindow">

<property name="geometry">

0

0

642

699

<property name="windowTitle">

客户端

<widget class="QWidget" name="centralWidget">

<widget class="QLabel" name="label">

<property name="geometry">

60

50

61

41

<property name="text">

IP地址:

<widget class="QLabel" name="label_2">

<property name="geometry">

280

50

61

41

<property name="text">

端口号:

<widget class="QLineEdit" name="lineEdit_IP">

<property name="geometry">

120

60

151

21

<widget class="QLineEdit" name="lineEdit_Port">

<property name="geometry">

340

60

113

21

<widget class="QPushButton" name="pushButton_Connect">

<property name="geometry">

480

50

93

31

<property name="text">

连接

<widget class="QPushButton" name="pushButton_Send">

<property name="geometry">

490

610

93

28

<property name="text">

发送

<widget class="QTextEdit" name="textEdit_Recv">

<property name="geometry">

60

150

521

261

<widget class="QTextEdit" name="textEdit_Send">

<property name="geometry">

60

490

521

101

<widget class="QLabel" name="label_3">

<property name="geometry">

60

110

131

31

<property name="font">

@H_273_404@

Agency FB

12

<property name="text">

聊天内容:

<widget class="QLabel" name="label_4">

<property name="geometry">

60

430

121

51

<property name="font">

@H_273_404@

Agency FB

12

<property name="text">

编辑消息:

<widget class="QMenuBar" name="menuBar">

<property name="geometry">

0

0

642

26

<widget class="QStatusBar" name="statusBar"/>

<layoutdefault spacing="6" margin="11"/>

        mainwindow.h

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include

#include

namespace Ui {

class MainWindow;

}

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0);

~MainWindow();

private slots:

void on_pushButton_Connect_clicked();

void on_pushButton_Send_clicked();

void socket_Read_Data();

void socket_Disconnected();

private:

Ui::MainWindow *ui;

QTcpSocket *socket;

};

#endif // MAINWINDOW_H

        main.cpp

#include “mainwindow.h”

#include

int main(int argc,argv);

MainWindow w;

w.show();

return a.exec();

}

        mainwindow.cpp

#include “mainwindow.h”

#include “ui_mainwindow.h”

#include “QTcpSocket”

#include “QMessage@R_922_2419@”

#include “QTextCodec.h”

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent),

ui(new Ui::MainWindow)

{

ui->setupUi(this);

setFixedSize(642,700); // 禁止改变窗口大小。

socket=new QTcpSocket();

//连接信号槽

QObject::connect(socket,&MainWindow::socket_Disconnected);

ui->textEdit_Recv->setReadOnly(true);

ui->pushButton_Send->setEnabled(false);

ui->lineEdit_IP->setText(“127.0.0.1”);

ui->lineEdit_Port->setText(“6000”);

}

MainWindow::~MainWindow()

{

delete this->socket;

delete ui;

}

void MainWindow::on_pushButton_Connect_clicked()

{

if(ui->pushButton_Connect->text() == tr(“连接”))

{

QString IP;

int port;

//获取IP地址

IP = ui->lineEdit_IP->text();

//获取端口号

port = ui->lineEdit_Port->text().toInt();

ui->textEdit_Recv->setText(“”);

ui->textEdit_Recv->insertPlainText(“正在连接”+ui->lineEdit_IP->text()+”:”+ui->lineEdit_Port->text()+”n”);

//取消已有的连接

socket->abort();

//连接服务器

socket->connectToHost(IP,port);

//等待连接成功

if(!socket->waitForConnected(30000))

{

QTextCodec::setCodecForLocale(QTextCodec::codecForName(“GB18030”));

QMessage@R_922_2419@::warning(this,tr(“连接失败!请重新连接”),QMessage@R_922_2419@::Yes);

ui->textEdit_Recv->insertPlainText(“连接失败n”);

return;

}

ui->textEdit_Recv->insertPlainText(“连接成功n”);

QTextCodec::setCodecForLocale(QTextCodec::codecForName(“GB18030”));

QMessage@R_922_2419@::information(this,tr(“连接成功”),QMessage@R_922_2419@::Yes);

//发送按键使能

ui->pushButton_Send->setEnabled(true);

//修改按键文字

ui->pushButton_Connect->setText(“断开连接”);

}

else

{

//断开连接

ui->textEdit_Recv->setText(“断开连接n”);

socket->disconnectFromHost();

//修改按键文字

ui->pushButton_Connect->setText(“连接”);

ui->pushButton_Send->setEnabled(false);

}

}

void MainWindow::on_pushButton_Send_clicked()

{

ui->textEdit_Recv->insertPlainText(tr(“李青说:”)+ui->textEdit_Send->toPlainText()+”n”);

socket->write(ui->textEdit_Send->toPlainText().toLocal8Bit());

socket->flush();

ui->textEdit_Send->setText(“”);

}

void MainWindow::socket_Read_Data()

{

QByteArray buffer;

//读取缓冲区数据

buffer = socket->readAll();

if(!buffer.isEmpty())

{

QString str = QString::fromLocal8Bit(buffer);

ui->textEdit_Recv->insertPlainText(“服务器消息:”+str+”n”);

}

}

void MainWindow::socket_Disconnected()

{

//发送按键失能

ui->pushButton_Send->setEnabled(false);

//修改按键文字

ui->pushButton_Connect->setText(“连接”);

qDebug() << "Disconnected!";

}

测试结果