https://www.programmersought.com/article/74196226714/
Server source code
main.qml
import QtQuick 2.4
import QtQuick.Window 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import Server_support 1.0
Window{
id:window
width:800
height:550
Rectangle{
width:800
height:550
gradient: Gradient {//Gradient attribute
GradientStop{//The color gradient from top to bottom, you need to use the attribute rotation to set 90 from the right.
position: 0.0; color: "white"
}
GradientStop{
position: 1.0; color: "pink"
}
}
}
visible:true
ListView{
add: Transition {
//Set the animation when adding Item from 100, 100 to is not set, it is the default in ListView
NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
}
id:listview
anchors.top: parent.top
anchors.bottom: bottomrect.top
anchors.left:parent.left
anchors.right:parent.right
//anchors' object is either a sibling node (provide id)
//Either the child node (parent)
delegate:Component{ //here delegate: must be followed by Component
Rectangle{
width:550
height:label1.height+20
opacity: 0.8
// color:"black"
BorderImage{
//Chat bubble
id:qipao
source:"pao.png"
width:label1.width+30
height:label1.height<=30?30:label1.height+5
// width:20
border.left: 10
border.right: 10
border.top:10
border.bottom:10
x:parent.width-width
Text{
id:label1
/*************
Here is the focus of the variable size chat bubble
According to the length of the obtained value and the pixels occupied by each word
*************/
width:label1.text.length<=10?label1.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //Multi-line text will wrap automatically if it exceeds the width
text:detail
}
}
}
}
model:ListModel{
id:listmodel
ListElement{
detail:"This is Server."
}
}
}
//Right
Rectangle {
id:message3
border.color: "black"
anchors.right: parent.right
anchors.top: window.top
height:510
width:window.width*0.3
gradient: Gradient {//Gradient attribute
GradientStop{//The color gradient from top to bottom, you need to use the attribute rotation to set 90 from the right.
position: 0.0; color: "red"
}
GradientStop{
position: 1.0; color: "blue"
}
}
}
Label{
id:label2;
text:"value";
anchors.top: message3.top
x:570
}
//Bottom
Rectangle{
height: 40
width: 550
anchors.bottom: parent.bottom
id:bottomrect
Row{
TextField{
id:textfield
placeholderText: "input content"
height:bottomrect.height
width:window.width*0.7
}
}
Button{
id:button1
text:"Send"
x:560
anchors.top: message3.bottom
onClicked: {
listmodel.append({"detail":textfield.text})
var result = textfield.text
server.sendMessage(result);
//textfield.text='' Clear the input box after sending the message
}
}
Button
{
id:button2
text:"Connect"
anchors.left: button1.right
onClicked:
{
label2.text=server.mystring
server.connect_client();
}
//Connect the signal to qml
Connections{
target:server;
onMystringChanged:label2.text = str;
}
}
Button
{
id:button3
text:"Quit"
anchors.left: button2.right
onClicked:
{
window.close();
}
}
}
Server
{
id:server
}
//Receive the message on the left
// Label{
// id:recv_message_window;
// text:"recv_message";
// width:275
// height:550
// y:20
// Button
// {
// id:button4
// text:"recvmsg"
// x:750
// y:490
// //anchors.right: window.right
// onClicked:
// {
// recv_message_window.text=server.message
// }
// Connections{
// target:server;
// onMessageChanged: recv_message_window.text = str;
// }
// }
// }
ListView{
width:200
height:550
add: Transition {
//Set the animation when adding Item from 100, 100 to is not set, it is the default in ListView
NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
}
id:listview2
anchors.top: parent.top
anchors.bottom: bottomrect.top
anchors.left:parent.left
anchors.right:parent.right
//anchors' object is either a sibling node (provide id)
//Either the child node (parent)
delegate:Component{ //here delegate: must be followed by Component
Rectangle{
width:200
height:label3.height
opacity: 0.8
BorderImage{
//Chat bubble
id:qipao
source:"pao.png"
width:label3.width+30
height:label3.height<=30?30:label3.height+5
// width:20
border.left: 10
border.right: 10
border.top:10
border.bottom:10
x:parent.width-width
Text{
id:label3
/*************
Here is the focus of the variable size chat bubble
According to the length of the obtained value and the pixels occupied by each word
*************/
width:label3.text.length<=10?label3.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //Multi-line text will wrap automatically if it exceeds the width
text:detail
}
}
}
}
model:ListModel{
id:listmodel2
ListElement{
detail:""
}
}
}
Rectangle{
anchors.top: parent.top
id:bottomrect2
Row{
Text{
id:textfield2
height:bottomrect2.height
width:window.width*0.7
// anchors.top : ListView.top
}
}
}
Button
{
id:button4
text:"recvmsg"
x:720
y:510
anchors.left: button3.right
onClicked:
{
listmodel2.append({"detail":textfield2.text})
textfield2.text=server.message
}
Connections{
target:server;
onMessageChanged: textfield2.text = str;
}
}
}
server.cpp
#include "server.h"
static SOCKET slisten;
static SOCKET sClient;
Server::Server(QObject *parent):QObject(parent)
{
m_pstring = new QString;
recv_data = new QString;
send_data = new QString;
}
Server::~Server()
{
delete this->m_pstring;
}
QString Server::getString()
{
setString("waiting connect...");
return *(this->m_pstring);
}
void Server::setString(QString string){
*(this->m_pstring)=string;
emit mystringChanged(string);//Send signal
}
QString Server::message()
{
return *recv_data;
}
void Server::setMessage(QString string)
{
*recv_data=string;
}
void Server::sendMessage(QString string)
{
*send_data = string;
qDebug()<<*send_data;
}
void Server::connect_client()
{
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
cout<<"socket error !"<<endl;
exit(0);
}
else
cout<<"socket suceess."<<endl;
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8080);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
cout << "bind error !" << endl;
}
else
cout<<"bind success."<<endl;
if (listen(slisten, 5) == SOCKET_ERROR)
{
cout << "listen error !"<< endl;
exit(0);
}
else
cout<<"listen succeed."<<endl;
start_thread1();
}
void Server::recvsend_message()
{
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
int count=0;
start_thread2();
while(true)
{
count++;
if(count==1)
{
cout<<"wating connect..."<<endl;
}
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if(count==1)
{
cout<<"accept one client."<<endl;
}
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
if(count == 1)
{
cout <<"accepted a connection:"<< inet_ntoa(remoteAddr.sin_addr) << endl;
}
cout << inet_ntoa(remoteAddr.sin_addr)<<":";
send_news();
}
closesocket(sClient);
WSACleanup();
}
void Server::recv_news()
{
char revData[255];
int ret = 0;
int count = 0;
while(true)
{
ret = recv(sClient, revData, 255, 0);
count++;
if(count == 1)
{
cout<<"recv success!"<<endl;
}
if (ret > 0)
{
revData[ret] = '\0';
*recv_data = QString(QLatin1String(revData));
qDebug()<<*recv_data;
emit messageChanged(*recv_data);
cout<<"recv a massgae:"<<revData<<endl;
}
}
}
void Server::send_news()
{
char sendbuffer[255];
while(true)
{
if((*send_data).size() > 0)
{
QByteArray buffer = (*send_data).toUtf8();
char *sendData = buffer.data();
strcpy(sendbuffer,sendData);
sendbuffer[strlen(sendbuffer)]='\0';
cout<<sendbuffer<<endl;
send(sClient, sendbuffer, strlen(sendbuffer), 0);
*send_data = "\0";
memset(sendbuffer,0,255);
}
}
}
void Server::start_thread1()
{
std::thread t1(&Server::thread1,this);
t1.detach();
}
void* Server::thread1(void *this_)
{
Server *_this = (Server*) this_;
_this->recvsend_message();
return _this;
}
void Server::start_thread2()
{
std::thread t2(&Server::thread2,this);
t2.detach();
}
void* Server::thread2(void *this_)
{
Server *_this = (Server*) this_;
_this->recv_news();
return _this;
}
void Server::start_thread3()
{
std::thread t3(&Server::thread3,this);
t3.detach();
}
void* Server::thread3(void *this_)
{
Server *_this = (Server*) this_;
_this->send_news();
return _this;
}
server.h
#ifndef SERVER_H
#define SERVER_H
#include <QObject>
#include<QString>
#include<qdebug.h>
#include<iostream>
#include<windows.h>
#include<thread>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
class Server : public QObject
{
Q_OBJECT
//Exposed to QML attributes
Q_PROPERTY(QString mystring READ getString WRITE setString NOTIFY mystringChanged)
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
public:
explicit Server(QObject *parent = 0);
Q_INVOKABLE QString getString(); //Q_INVOCABLE is exposed to QML
Q_INVOKABLE void setString(QString string);
Q_INVOKABLE QString message();
Q_INVOKABLE void setMessage(QString string);
Q_INVOKABLE void sendMessage(QString string);
~Server();
Q_INVOKABLE void connect_client();
void recvsend_message();
void recv_news();
void send_news();
void start_thread1();
void start_thread2();
void start_thread3();
static void* thread1(void* this_);
static void* thread2(void* this_);
static void* thread3(void* this_);
signals:
void mystringChanged(QString value); //signal
void messageChanged(QString str);
public:
QString *m_pstring;
QString *recv_data;
QString *send_data;
};
#endif // SERVER_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include<QtQml>
#include"server.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//Register to QML
qmlRegisterType<Server>("Server_support",1,0,"Server");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Client source code
main.qml
import QtQuick 2.4
import QtQuick.Window 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import RegisterClient 1.0
Window{
id:window
width:800
height:550
Rectangle{
width:800
height:550
gradient: Gradient {//Gradient attribute
GradientStop{//The color gradient from top to bottom, you need to use the attribute rotation to set 90 from the right.
position: 0.0; color: "white"
}
GradientStop{
position: 1.0; color: "pink"
}
}
}
visible:true
ListView{
add: Transition {
//Set the animation when adding Item from 100, 100 to is not set, it is the default in ListView
NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
}
id:listview
anchors.top: parent.top
anchors.bottom: bottomrect.top
anchors.left:parent.left
anchors.right:parent.right
//anchors' object is either a sibling node (provide id)
//Either the child node (parent)
delegate:Component{ //here delegate: must be followed by Component
Rectangle{
width:550
height:label1.height+20
opacity: 0.8
// color:"black"
BorderImage{
//Chat bubble
id:qipao
source:"pao.png"
width:label1.width+30
height:label1.height<=30?30:label1.height+5
// width:20
border.left: 10
border.right: 10
border.top:10
border.bottom:10
x:parent.width-width
Text{
id:label1
/*************
Here is the focus of the variable size chat bubble
According to the length of the acquired value and the pixels occupied by each word
*************/
width:label1.text.length<=10?label1.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //Multi-line text will wrap automatically if it exceeds the width
text:detail
}
}
}
}
model:ListModel{
id:listmodel
ListElement{
detail:"This is Client."
}
}
}
//Right
Rectangle {
id:message3
border.color: "black"
anchors.right: parent.right
anchors.top: window.top
height:510
width:window.width*0.3
gradient: Gradient {//Gradient attribute
GradientStop{//The color gradient from top to bottom, you need to use the attribute rotation to set 90 from the right
position: 0.0; color: "red"
}
GradientStop{
position: 1.0; color: "blue"
}
}
}
Label{
id:label2;
text:"value";
anchors.top: message3.top
x:570
}
//Bottom
Rectangle{
height: 40
width: 550
anchors.bottom: parent.bottom
id:bottomrect
Row{
TextField{
id:textfield
placeholderText: "input content"
height:bottomrect.height
width:window.width*0.7
}
}
Button{
id:button1
text:"Send"
x:560
anchors.top: message3.bottom
onClicked: {
listmodel.append({"detail":textfield.text})
client.obtain_message(textfield.text);
//textfield.text=''
}
}
Client
{
id:client
}
Button
{
id:button2
text:"Connect"
anchors.left: button1.right
onClicked:
{
label2.text=client.mystring
client.connect_server();
//client.communication();
}
//Connect the signal to qml
Connections{
target:client;
onMystringChanged:label2.text = str;
//The value here is the parameter in the signal function
}
}
Button
{
id:button3
text:"Quit"
anchors.left: button2.right
onClicked:
{
window.close();
}
}
}
ListView{
width:200
height:550
add: Transition {
//Set the animation when adding Item from 100, 100 to is not set, it is the default in ListView
NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
}
id:listview2
anchors.top: parent.top
anchors.bottom: bottomrect.top
anchors.left:parent.left
anchors.right:parent.right
//anchors' object is either a sibling node (provide id)
//Either the child node (parent)
delegate:Component{ //here delegate: must be followed by Component
Rectangle{
width:200
height:label3.height
opacity: 0.8
BorderImage{
//Chat bubble
id:qipao
source:"pao.png"
width:label3.width+30
height:label3.height<=30?30:label3.height+5
// width:20
border.left: 10
border.right: 10
border.top:10
border.bottom:10
x:parent.width-width
Text{
id:label3
/*************
Here is the focus of the variable size chat bubble
According to the length of the obtained value and the pixels occupied by each word
*************/
width:label3.text.length<=10?label3.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //Multi-line text will wrap automatically if it exceeds the width
text:detail
}
}
}
}
model:ListModel{
id:listmodel2
ListElement{
detail:""
}
}
}
Rectangle{
anchors.top: parent.top
id:bottomrect2
Row{
Text{
id:textfield2
height:bottomrect2.height
width:window.width*0.7
// anchors.top : ListView.top
}
}
}
Button
{
id:button4
text:"recvmsg"
x:720
y:510
anchors.left: button3.right
onClicked:
{
listmodel2.append({"detail":textfield2.text})
textfield2.text=client.message
}
Connections{
target:client;
onMessageChanged: textfield2.text = str;
}
}
}
/*
Window {
width:200
height: 100
visible: true
Button{
id:btn1;
height:20;
width:60;
text:"button";
onClicked: {
//qml operation c++
//btn1.text=myobj.mystring;
label1.text = myobj.mystring;
}
}
Label{
id:label1;
text: "Initial value";
anchors.left:btn1.right;
//Create an object anchors.leftMargin: 12;
}
MyClassType
{
id:myobj;
}
//Connect the signal to qml
Connections{
target:myobj;
onMystringChanged:label1.text=value;
//The value here is the parameter in the signal function
}
}*/
client.cpp
#include"client.h"
static SOCKET sclient;
client::client(QObject *parent)
:QObject(parent)
{
this->m_pstring = new QString;
this->send_data = new QString;
this->recv_data = new QString;
}
client::~client()
{
delete this->m_pstring;
delete this->send_data;
delete this->recv_data;
}
QString client::get_send_data()
{
return *recv_data;
}
QString client::getString()
{
setString("connect succeed...");
return *(this->m_pstring);
}
void client::setString(QString string)
{
*(this->m_pstring)=string;
emit mystringChanged(string);//Send signal
}
void client::setMessage(QString string)
{
*recv_data=string;
}
void client::sendMessage(QString string)
{
*send_data = string;
qDebug()<<*send_data;
}
void client::obtain_message(QString string)
{
*send_data = string;
qDebug()<<*send_data;
}
void client::connect_server()
{
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if (WSAStartup(sockVersion, &data) != 0)
{
exit(0);
}
sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sclient == INVALID_SOCKET)
{
printf("invalid socket!");
exit(0);
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8080);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (::connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{
printf("connect error !");
closesocket(sclient);
exit(0);
}
cout<<"connect succeed..."<<endl;
start_thread1();
}
void client::communication()
{
/*
Create a separate thread for recv outside the loop
Send messages in a loop
*/
start_thread2();
char sendData[255];
while(true)
{
//cout<<"sendmessage:";
// cout<<"!!!!!!!!!!!!!!!!!!!!!!"<<endl;
if( (*send_data).size() > 0)
{
QByteArray buffer =(*send_data).toUtf8();
char *p =buffer.data();
strcpy(sendData,p);
sendData[strlen(sendData)]='\0';
cout<<sendData<<endl;
send(sclient, sendData, strlen(sendData), 0);
*send_data = "\0";
memset(sendData,0,strlen(sendData));
}
}
closesocket(sclient);
WSACleanup();
}
QString client::message()
{
return *recv_data;
}
void client::recv_news()
{
char recData[255];
int ret = 0;
int count = 0;
while(true)
{
cout<<"~~~~~~~~~~~~~~~~~~~~~~~~"<<endl;
ret = recv(sclient, recData, 255, 0);
count++;
if(count == 1)
{
cout<<"recv success!"<<endl;
}
if (ret>0)
{
recData[ret] = '\0';
cout<<"recv a messgae:"<<recData<<endl;
*recv_data = QString(QLatin1String(recData));
emit messageChanged(*recv_data);
}
*recv_data="\0";
memset(recData,0,255);
}
}
void client::send_news()
{
char sendData[255];
//char sendData[]="This client ,hello server";
//cout<<"!!!!!!!!!!!!!!!!!!!!!!"<<endl;
if((*send_data).size() > 0)
{
QByteArray buffer =(*send_data).toUtf8();
char *p =buffer.data();
strcpy(sendData,p);
sendData[strlen(sendData)]='\0';
cout<<sendData<<endl;
send(sclient, sendData, strlen(sendData), 0);
//cout<<"Now,send the message to server."<<endl;
*send_data = '\0';
memset(sendData,0,strlen(sendData));
}
}
void client::start_thread1()
{
cout<<"start communicate."<<endl;
std::thread t(&client::thread1,this);
t.detach();
}
void* client::thread1(void* _this)
{
client* p = (client*) _this;
p->communication();
return p;
}
void client::start_thread2()
{
std::thread t(&client::thread2,this);
t.detach();
}
void* client::thread2(void* _this)
{
client*p = (client*) _this;
p->recv_news();
return p;
}
void client::start_thread3()
{
std::thread t(&client::thread3,this);
t.detach();
}
void* client::thread3(void* _this)
{
client*p = (client*) _this;
p->send_news();
return p;
}
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QObject>
#include<QString>
#include<Qtimer.h>
#include<thread>
#include<Windows.h>
#include<qdebug.h>
#pragma comment(lib,"ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
using namespace std;
class client : public QObject
{
Q_OBJECT
//Exposed to QML attributes
Q_PROPERTY(QString mystring READ getString WRITE setString NOTIFY mystringChanged)
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
public:
explicit client(QObject *parent = 0);
~client();
Q_INVOKABLE QString getString(); //Q_INVOCABLE is exposed to QML
Q_INVOKABLE void setString(QString string);
Q_INVOKABLE QString message();
Q_INVOKABLE void setMessage(QString string);
Q_INVOKABLE void sendMessage(QString string);
Q_INVOKABLE void obtain_message(QString str);
Q_INVOKABLE QString get_send_data();
void communication();
void recv_news();
void send_news();
signals:
void mystringChanged(QString value); //signal
void messageChanged(QString str);
public slots:
Q_INVOKABLE void connect_server();
static void*thread1(void*);
static void*thread2(void*);
static void*thread3(void*);
void start_thread1();
void start_thread2();
void start_thread3();
public:
QString * m_pstring;
QString *send_data;
QString *recv_data;
};
#endif // MYCLASS_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include"client.h"
#include<QtQml>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<client>("RegisterClient",1,0,"Client");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
How to use the chat software:
1. After running Server and Client, first start the server and click Connect, then click the client Connect.
2. When the prompt on the upper right becomes waiting connect... and connect succeed, you can achieve duplex sending and receiving of messages.
ps: The upper left corner shows the real-time message. Click the recvmsg button to record the current message and display the message in the box.
Effect picture
Note: The chat box and the icon material can be selected arbitrarily