网络聊天程序的设计与实现
1、了解Socket通信的原理,学会使用Socket进行简单的网络编程,在此基础上编写一个聊天程序。
2、了解Qt编程,熟悉C++语言。
1、背景知识
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。用户数据报协议(UDP,User Datagram Protocol)是一种无需建立连接就可以发送封装的 IP 数据包的传输层通信协议。它不提供数据包分组、组装、对数据包进行排序、报文到达确认、流量控制等功能。
Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部;Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。
2、基本原理
Socket是一种“打开—读/写—关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方的内容,通讯结束时关闭文件。
Socket在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。
Socket保证了不同计算机之间的通信,对于网站,通信模型是服务器与客户端之间的通信。两端都建立一个socket对象,然后通过socket对象对数据进行传输。通常服务器处于一个无限循环,等待客户端的连接。
3、模块介绍
(1)服务器端:监听本网络上的所有Ip,如有连接请求,则通过连接,与客户端进行一对一通信。
(2)客户端:通过给定的端口号与Ip地址连接到服务器端,与服务器端进行通信。
4、设计步骤
(1)服务端
(2)客户端
1、程序流程图
2、代码
(1)服务器端
代码组成:
server.h
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class server; }
QT_END_NAMESPACE
class server : public QMainWindow
{
Q_OBJECT
public:
server(QWidget *parent = nullptr);
~server();
private:
Ui::server *ui;
QTcpServer *Server;
QTcpSocket *Socket;
private slots:
void Listen();
void send();
void newConnect();
void Read_data();
void DisConnect();
};
#endif // SERVER_H
main.cpp
#include "server.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
server w;
w.show();
return a.exec();
}
server.cpp
#include "server.h"
#include "ui_server.h"
#include
server::server(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::server)
{
ui->setupUi(this);
ui->Port->setText("2770");
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(Listen()));
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(send()));
}
server::~server()
{
delete ui;
}
void server::Listen(){
Server = new QTcpServer();
//int port = ui->Port->text().toInt();
if(!Server->listen(QHostAddress::Any, 2770))
{
QMessageBox::information(this, "QT网络通信", "服务器端监听失败!");
return;
}
else
{
QMessageBox::information(this, "QT网络通信", "服务器监听成功!");
}
connect(Server, SIGNAL(newConnection()), this, SLOT(newConnect()));
}
void server::send(){
qint64 writes=Socket->write(ui->textEdit->toPlainText().toLatin1());
bool Bool=Socket->flush();
if(writes==-1&&Bool!=1)
{
QMessageBox::warning(this, "QT网络通信", "发送数据失败,请重新发送!");
return;
}
ui->listWidget->addItem(QString(ui->textEdit->toPlainText()).toLocal8Bit());
ui->textEdit->clear();
}
void server::Read_data(){
QByteArray array;
array.resize(Socket->bytesAvailable());//根据可读数据来设置空间大小
array=Socket->readLine();
ui->listWidget->addItem("【客户端】:"+array);
}
void server::newConnect(){
Socket=Server->nextPendingConnection();
if(!Socket){
QMessageBox::warning(this, "warning", "未正确获取客户端连!");
return;
}
else
{
QMessageBox::information(this, "QT网络通信", "成功接受客户端的连接");
connect(Socket, SIGNAL(readyRead()), this, SLOT(Read_data()));
connect(Socket, SIGNAL(disconnected()), this, SLOT(DisConnect()));
}
}
void server::DisConnect(){
QMessageBox::information(this, "QT网络通信", "与客户端的连接断开");
return;
}
server.ui
server
0
0
465
556
server
20
410
301
51
340
440
75
23
发送
20
40
81
20
服务端端口号
110
40
113
20
240
40
75
23
初始化
20
80
301
311
0
0
465
23
(2)客户端
代码组成:
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class client; }
QT_END_NAMESPACE
class client : public QMainWindow
{
Q_OBJECT
public:
client(QWidget *parent = nullptr);
~client();
private:
Ui::client *ui;
private slots:
void connection();
void send();
void read_data();
private:
QTcpSocket *Socket;
};
#endif // CLIENT_H
main.cpp
#include "client.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
client w;
w.show();
return a.exec();
}
client.cpp
#include "client.h"
#include "ui_client.h"
#include
#include
#include
client::client(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::client)
{
ui->setupUi(this);
ui->Ip->setText("127.0.0.1");
ui->Port->setText("2770");
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(connection()));
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(send()));
}
client::~client()
{
delete ui;
}
void client::connection(){
Socket = new QTcpSocket(this);
QString ip = ui->Ip->text();
// int port = ui->Port->text().toInt();
Socket->connectToHost(QHostAddress("127.0.0.1"), 2770);
if(!Socket->waitForConnected(30000))
{
QMessageBox::warning(this, "warning", "连接服务端失败!");
return;
}
QMessageBox::information(this,"Qt","success!");
connect(Socket, SIGNAL(readyRead()), this, SLOT(read_data()));
}
void client::send(){
//获取TextEdit控件中的内容
QString data=ui->send->toPlainText().toUtf8();
QByteArray ba=data.toLocal8Bit();
int sendRe = Socket->write(ba);
bool bools=Socket->flush();
if(sendRe == -1&&bools!=1)
{
QMessageBox::information(this, "QT网络通信", "向服务端发送数据失败!");
return;
}
ui->listWidget->addItem(QString(ui->send->toPlainText()).toLocal8Bit());
ui->send->clear();
}
void client::read_data(){
QByteArray array;
array.resize(Socket->bytesAvailable());//根据可读数据来设置空间大小
array=Socket->readLine();
ui->listWidget->addItem("【服务端】:"+array);
}
client.ui
client
0
0
373
573
client
20
30
61
21
服务端IP
20
70
81
21
服务端端口号
100
30
113
20
100
70
113
20
20
360
271
51
240
50
75
23
连接
250
430
75
23
发送
20
100
271
241
0
0
373
23
1、实验结果
2、分析
首先必须要打开服务端,让服务器建立监听,然后再打开客户端,与服务器建立连接,客户端可以向服务器先发送消息,然后收到服务器的回复后又可以继续发送。有一个缺点是,必须要一来一回的聊天,客户端不能向服务器发送多条消息,同样服务器也不能。
本实验是第一个实验,刚开始拿到这题,完全没有一点思路,看了附录一后,逐渐对网络通信有了一点了解。在此之前我们没有接触过使用socket接口实现网络协议的一系列编程思想,对TCP和UDP的了解也仅限于理论知识,完全不会运用到实验中。在实验一中,我学会使用socket实现通信,也了解了一系列编程协议,同时从整体上对于winsock的使用流程有了更深一步的了解,通过阅读源码,对于TCP/IP协议在程序运行的实质和流程有了根深的理解。