Server源碼
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 { //漸變屬性
GradientStop{//從上到下顏色漸變,需要從做到右使用屬性rotation設置90即可
position: 0.0; color: "white"
}
GradientStop{
position: 1.0; color: "pink"
}
}
}
visible:true
ListView{
add: Transition {
//設置增加Item時的動畫 from 100,100 to不設置 就是默認在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的對象要麼是兄弟結點(提供id)
//要麼就是子節點(parent)
delegate:Component{ //這裏delegate:後面必須是Component
Rectangle{
width:550
height:label1.height+20
opacity: 0.8
// color:"black"
BorderImage{
//聊天氣泡
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
/*************
此處爲可變大小的聊天氣泡的重點
根據獲取值的長度 和每個字佔的像素
*************/
width:label1.text.length<=10?label1.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //多行文本 超過width就自動換行
text:detail
}
}
}
}
model:ListModel{
id:listmodel
ListElement{
detail:"This is Server."
}
}
}
//右側
Rectangle {
id:message3
border.color: "black"
anchors.right: parent.right
anchors.top: window.top
height:510
width:window.width*0.3
gradient: Gradient { //漸變屬性
GradientStop{//從上到下顏色漸變,需要從做到右使用屬性rotation設置90即可
position: 0.0; color: "red"
}
GradientStop{
position: 1.0; color: "blue"
}
}
}
Label{
id:label2;
text:"value";
anchors.top: message3.top
x:570
}
//底部
Rectangle{
height: 40
width: 550
anchors.bottom: parent.bottom
id:bottomrect
Row{
TextField{
id:textfield
placeholderText: "輸入內容"
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='' 發送消息後清空輸入框
}
}
Button
{
id:button2
text:"Connect"
anchors.left: button1.right
onClicked:
{
label2.text=server.mystring
server.connect_client();
}
//連接信號 到qml
Connections{
target:server;
onMystringChanged:label2.text = str;
}
}
Button
{
id:button3
text:"Quit"
anchors.left: button2.right
onClicked:
{
window.close();
}
}
}
Server
{
id:server
}
//左側接受消息
// 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 {
//設置增加Item時的動畫 from 100,100 to不設置 就是默認在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的對象要麼是兄弟結點(提供id)
//要麼就是子節點(parent)
delegate:Component{ //這裏delegate:後面必須是Component
Rectangle{
width:200
height:label3.height
opacity: 0.8
BorderImage{
//聊天氣泡
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
/*************
此處爲可變大小的聊天氣泡的重點
根據獲取值的長度和每個字佔的像素
*************/
width:label3.text.length<=10?label3.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //多行文本 超過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);//發送信號
}
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
//暴露給QML 屬性
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暴露給 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); //信號
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);
//註冊到QML
qmlRegisterType<Server>("Server_support",1,0,"Server");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
客戶端源碼
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 { //漸變屬性
GradientStop{//從上到下顏色漸變,需要從做到右使用屬性rotation設置90即可
position: 0.0; color: "white"
}
GradientStop{
position: 1.0; color: "pink"
}
}
}
visible:true
ListView{
add: Transition {
//設置增加Item時的動畫 from 100,100 to不設置 就是默認在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的對象要麼是兄弟結點(提供id)
//要麼就是子節點(parent)
delegate:Component{ //這裏delegate:後面必須是Component
Rectangle{
width:550
height:label1.height+20
opacity: 0.8
// color:"black"
BorderImage{
//聊天氣泡
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
/*************
此處爲可變大小的聊天氣泡的重點
根據獲取值的長度 和每個字佔的像素
*************/
width:label1.text.length<=10?label1.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //多行文本 超過width就自動換行
text:detail
}
}
}
}
model:ListModel{
id:listmodel
ListElement{
detail:"This is Client."
}
}
}
//右側
Rectangle {
id:message3
border.color: "black"
anchors.right: parent.right
anchors.top: window.top
height:510
width:window.width*0.3
gradient: Gradient { //漸變屬性
GradientStop{//從上到下顏色漸變,需要從做到右使用屬性rotation設置90即可
position: 0.0; color: "red"
}
GradientStop{
position: 1.0; color: "blue"
}
}
}
Label{
id:label2;
text:"value";
anchors.top: message3.top
x:570
}
//底部
Rectangle{
height: 40
width: 550
anchors.bottom: parent.bottom
id:bottomrect
Row{
TextField{
id:textfield
placeholderText: "輸入內容"
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();
}
//連接信號 到qml
Connections{
target:client;
onMystringChanged:label2.text = str;
//這裏的value是signal信號函數裏面的參數
}
}
Button
{
id:button3
text:"Quit"
anchors.left: button2.right
onClicked:
{
window.close();
}
}
}
ListView{
width:200
height:550
add: Transition {
//設置增加Item時的動畫 from 100,100 to不設置 就是默認在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的對象要麼是兄弟結點(提供id)
//要麼就是子節點(parent)
delegate:Component{ //這裏delegate:後面必須是Component
Rectangle{
width:200
height:label3.height
opacity: 0.8
BorderImage{
//聊天氣泡
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
/*************
此處爲可變大小的聊天氣泡的重點
根據獲取值的長度和每個字佔的像素
*************/
width:label3.text.length<=10?label3.text.length*12:120
font.pixelSize: 12
anchors.centerIn:parent
anchors.leftMargin:10
wrapMode: Text.Wrap //多行文本 超過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操作c++
//btn1.text=myobj.mystring;
label1.text = myobj.mystring;
}
}
Label{
id:label1;
text:"初始值";
anchors.left:btn1.right;
//創建對象 anchors.leftMargin: 12;
}
MyClassType
{
id:myobj;
}
//連接信號 到qml
Connections{
target:myobj;
onMystringChanged:label1.text=value;
//這裏的value是signal信號函數裏面的參數
}
}*/
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);//發送信號
}
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()
{
/*
循環外先給recv創建單獨的線程
循環發消息
*/
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
//暴露給QML 屬性
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暴露給 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); //信號
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();
}
聊天小軟件使用方法:
1.運行Server與Client後,先啓動服務器點擊Connect,然後點擊客戶端Connect。
2.當右上方提示變爲waiting connect....與connect succeed後可以實現雙工發送與接收消息。
ps:左上角爲顯示的爲實時消息,點擊recvmsg按鈕可以把當前消息記錄並顯示消息在框中。
效果圖
注:聊天框,與圖標的素材可以任意選擇