QT chatroom First Demo
成品演示:
整体项目结构:
1.开启电脑telnet,当作一个客户端
客户端还没开始写,先暂时用
2.编写服务端server
下面可不看:自己的理解
socket套接字=lp地址+端口号
目标:实现一个简单的服务器,可以接受客户端发来的信息今天的客户端采用telnet客户端服务端: 一个客户端
类库
QTcpServer实现服务器端的端口监听,可以提供一种服务
QTcpSocket 实现套接字类,这里有消息的封装,可以读取和写入
自定义类Server类,用户消息服务。
工程文件引入network类库才能使用tcp服务
如何开启端口服务?
使用QTcpServer中的listen方法就可以开启指定的端口listen(QHostAddress::Any,8888);
并在mianwindow.h里面实例化 server server;
通过重写void incomingConnection(qintptr handle);来将新连接用户加入进来
将当前的连接对象存入到socket对象中sock=new QTcpSocket(this);sock->setSocketDescriptor(handle);监听sock对象的readyread信号来处理最后的消息读取消息
sock->read(buffer,sock->bytesAvailable()):通过if语句来排除无用字符if(sock->bytesAvailable()>0)
2.1创建widget项目
新的c++文件server,server继承QObject,然后工程文件引入network类库,QObject改为QTcpServer
.
2.2开启端口服务
- 使用QTcpServer中的listen方法就可以开启指定的端口listen(QHostAddress::Any,8888);
- 并在widget.h里面实例化 server server;
Server::Server(QObject *parent) : QTcpServer(parent)
{
//开启监听
listen(QHostAddress::Any,8888);
}
class Widget : public QWidget
{
private:
Ui::Widget *ui;
Server server;//实例化
};
打开cmd,检查是否开启端口成功
2.3监听链接的用户
QTCPserver
void newConnection(); 单线程的的信号,不推荐使用
重写void incomingConnection(qintptr handle);来监听链接的用户,推荐使用 后台线程的方式
virtual void incomingConnection(qintptr handle);
This virtual function is called by QTcpServer when a new connection is available. The socketDescriptor argument is the native socket descriptor for the accepted connection.
就是说当有人连接你的服务端时,就会把连接者的socket信息都保存在socketDescriptor(即handle)当中
server.h当中
private slots:
void incomingConnection(qintptr handle);
server.cpp
//只要有一个人链接,就会调用这个方法
void Server::incomingConnection(qintptr handle)
{
qDebug() <<"有人链接了";
}
打开cmd 输入telnet 127.0.0.1 8888
,链接成功显示
2.4接受连接用户的信息
void Server::incomingConnection(qintptr handle)
{
// qDebug() <<"有人链接了";
sock = new QTcpSocket(this);
sock->setSocketDescriptor(handle);//将连接的用户加入进来,就可以接受用户输入的内容
connect(sock,&QTcpSocket::readyRead,[=](){
char buffer[1024]; //暂存
sock->read(buffer,sock->bytesAvailable()); //读取的有效内容存到buffer
qDebug() << buffer;
});
}
cmd 输入 telnet 127.0.0.1 8888
,连接成功后,在cmd黑窗口输入内容,会显示在QT控制窗口当中
3.编写客户端
下面可不看:自己的理解
textEdit 富文本框 html也行,消息窗口
plain textedit 只有文本功能
lineEdit单行文本框
listWidget列表界面做好后要先编译,然后才可以正常使用控件编译快捷键ctrl+b,显示日志
客户端接收数据信号readyRead()
信息读取socket->readAll():
将bytearray转为字符串arr.data();
发送消息socket->write(str.toUtf80);采用字节流发送
3.1UI
创建一个client UI
client.h
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class Client;
}
class Client : public QWidget
{
Q_OBJECT
public:
explicit Client(QWidget *parent = 0);
~Client();
private slots:
void on_connetBtn_clicked();
void receiveMessage();
void on_sendBtn_clicked();
private:
Ui::Client *ui;
QTcpSocket * socket; //必须new才能实例化
};
client.cpp
Client::Client(QWidget *parent) :
QWidget(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
setWindowTitle("TCP客户端");
}
Client::~Client()
{
delete ui;
}
void Client::on_connetBtn_clicked()
{
socket = new QTcpSocket(this);
//连接服务器 IP+端口
socket->connectToHost(ui->IPlineEdit->text(),ui->portlineEdit->text().toInt());
//接收服务端发来的消息
connect(socket,&QTcpSocket::readyRead,this,[=](){
this->receiveMessage();
});
}
//接收服务端来的信息
void Client::receiveMessage()
{
QByteArray receivedata = socket->readAll(); //接受的是字符流
QString str = receivedata.data(); //字符流转换为普通文本
ui->receivetextEdit->setText(str);
}
//客户端发送信息出去
void Client::on_sendBtn_clicked()
{
QString sendMessage = ui->sendtextEdit->toPlainText();//普通文本
socket->write(sendMessage.toUtf8()); //toUtf8()中文不乱码
}
3.2在主窗口写一个跳转页面按钮
方便测试客户端功能
widget.ui拉一个按钮上去,然后,提升槽为clicked
widget.h 创建client指针
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
Server server;//实例化,相当于new了
Client *client;
};
widget.cpp
//显示客户端界面
void Widget::on_pushButton_clicked()
{
client = new Client(this);
client->show();
}
3.3测试客户端发送功能
打开服务端端口后,连接服务端后,点击发送消息
4.服务端
4.1增加UI
4.2接收客户端的信息,显示用户上线日志
-
为了与UI连接,
server.h
新增加一个server (int port)
构造函数,当点击开始监听这个控件时,才开启端口server.cpp
Server::Server(QObject *parent) : QTcpServer(parent) { // //开启监听 // listen(QHostAddress::Any,8888); } Server::Server(int port) { //开启监听 listen(QHostAddress::Any,port); }
widget.cpp
void Widget::on_listenPortBtn_clicked() { int port = ui->portlineEdit->text().toInt(); server = new Server(port); //开启监听之后,就可以处理客户端发来的信息 connect(server,&Server::translateClientMessage,this,&Widget::getClientMessage); }
-
处理来自客户端的消息
server.h
增加一个translateClientMessage(QString,int)
的自定义信号class Server : public QTcpServer { Q_OBJECT public: explicit Server(QObject *parent = nullptr); Server(int port); private: QTcpSocket *sock; //套接字,当别人连上你这个服务端时后,对方的ip地址等的一些信息就存在这里面 signals: //自定义接受信号,服务器接收到了客户端的自身socket信息和传过来的文本数据 void translateClientMessage(QString,int); //1表示文本信息,2表示显示client IP信息 private slots: void incomingConnection(qintptr handle); void receiveMessage(); };
server.cpp
//只要有一个人链接,就会调用这个方法 void Server::incomingConnection(qintptr handle) { qDebug() <<"有人链接了"; sock = new QTcpSocket(this); sock->setSocketDescriptor(handle);//将连接的用户加入进来,就可以接受用户输入的内容 connect(sock,&QTcpSocket::readyRead,[=](){ this->receiveMessage(); }); //获取对方的ip地址 QString clientIP = sock->peerAddress().toString(); emit translateClientMessage(clientIP + "上线了",2); } void Server::receiveMessage() { // if(sock->bytesAvailable() >0) // { // char buffer[1024]; //暂存 // sock->read(buffer,sock->bytesAvailable()); //读取的有效内容存到buffer // qDebug() << buffer; // } QByteArray byteArrData = sock->readAll(); QString str = byteArrData.data(); //接收到客户端的发送文本后,触发转发的信号,将这些文本来显示到窗口上 //有了信号,就要有相应的槽函数,我们要把这些接收的信息显示在窗口UI上,所以槽函数应该写在widget上 emit translateClientMessage(str,1); //1用来表示接受的是客户端发来的消息 }
widget.cpp 定义了一个槽函数
getClientMessage(QString s,int type)
来实现client socket的接收,显示到UI上void Widget::getClientMessage(QString s,int type) { if(type == 1) { ui->receivetextEdit->setText(s); //显示接收的客户端的信息文本 } else if(type == 2) { ui->conneterLoglistWidget->addItem(s);//显示连接日志,客户端的IP } }
这里写完,自己试一下,能否运行
4.3服务端发送消息给客户端
由于在server.h
当中定义的是private:QTcpSocket *sock;
,服务端的发送是在widget主窗口上的,所以是无法使用到这个sock
,
不可以在widget.cpp
当中访问到server.h
这个私有属性
server.h
增加一个公共接口:public:QTcpSocket * getSock();
server.cpp
直接返回这个sock就行
QTcpSocket *Server::getSock()
{
return this->sock;
}
widget.cpp 提升发送 这个按钮的点击函数
void Widget::on_sendBtn_clicked()
{
//先获取控件的内容,在发送
QByteArray serverSendMessage = ui->sendtextEdit->toPlainText().toUtf8();
sock = server->getSock();
sock->write(serverSendMessage);
}