├── .gitignore
├── ChatServer.pro
├── README.md
├── image
├── 9.jpg
└── ButtonImage
│ ├── add_hover.png
│ ├── add_normal.png
│ ├── close_down.png
│ ├── close_hover.png
│ ├── close_normal.png
│ ├── menu_down.png
│ ├── menu_hover.png
│ ├── menu_normal.png
│ ├── min_down.png
│ ├── min_hover.png
│ ├── min_normal.png
│ ├── minus_hover.png
│ └── minus_normal.png
├── qml.qrc
├── qml
├── CusButton.qml
├── MoveMouseArea.qml
├── ResizeMouseArea.qml
└── main.qml
├── src
├── chatsocket.cpp
├── chatsocket.h
├── chattcpserver.cpp
├── chattcpserver.h
├── database.cpp
├── database.h
├── framelesswindow.cpp
├── framelesswindow.h
├── main.cpp
├── protocol.cpp
└── protocol.h
├── test.sql
└── users
├── 843261040
└── headImage
│ └── head1.jpg
└── users.db
/.gitignore:
--------------------------------------------------------------------------------
1 | users/
2 | debug/
3 | release/
4 | *.user
5 | *.Debug
6 | *.Release
7 | Makefile
8 | *.stash
9 | *.rc
10 | *.exe
11 | *.qmlc
--------------------------------------------------------------------------------
/ChatServer.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += qml quick network sql
4 |
5 | CONFIG += c++11
6 |
7 | HEADERS += \
8 | src/chattcpserver.h \
9 | src/protocol.h \
10 | src/chatsocket.h \
11 | src/framelesswindow.h \
12 | src/database.h
13 |
14 | SOURCES += \
15 | src/main.cpp \
16 | src/chattcpserver.cpp \
17 | src/chatsocket.cpp \
18 | src/framelesswindow.cpp \
19 | src/database.cpp \
20 | src/protocol.cpp
21 |
22 | RESOURCES += qml.qrc
23 |
24 | # Additional import path used to resolve QML modules in Qt Creator's code model
25 | QML_IMPORT_PATH =
26 |
27 | # Additional import path used to resolve QML modules just for Qt Quick Designer
28 | QML_DESIGNER_IMPORT_PATH =
29 |
30 | # The following define makes your compiler emit warnings if you use
31 | # any feature of Qt which as been marked deprecated (the exact warnings
32 | # depend on your compiler). Please consult the documentation of the
33 | # deprecated API in order to know how to port your code away from it.
34 | DEFINES += QT_DEPRECATED_WARNINGS
35 |
36 | # You can also make your code fail to compile if you use deprecated APIs.
37 | # In order to do so, uncomment the following line.
38 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
39 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
40 |
41 | # Default rules for deployment.
42 | qnx: target.path = /tmp/$${TARGET}/bin
43 | else: unix:!android: target.path = /opt/$${TARGET}/bin
44 | !isEmpty(target.path): INSTALLS += target
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/README.md
--------------------------------------------------------------------------------
/image/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/9.jpg
--------------------------------------------------------------------------------
/image/ButtonImage/add_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/add_hover.png
--------------------------------------------------------------------------------
/image/ButtonImage/add_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/add_normal.png
--------------------------------------------------------------------------------
/image/ButtonImage/close_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/close_down.png
--------------------------------------------------------------------------------
/image/ButtonImage/close_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/close_hover.png
--------------------------------------------------------------------------------
/image/ButtonImage/close_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/close_normal.png
--------------------------------------------------------------------------------
/image/ButtonImage/menu_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/menu_down.png
--------------------------------------------------------------------------------
/image/ButtonImage/menu_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/menu_hover.png
--------------------------------------------------------------------------------
/image/ButtonImage/menu_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/menu_normal.png
--------------------------------------------------------------------------------
/image/ButtonImage/min_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/min_down.png
--------------------------------------------------------------------------------
/image/ButtonImage/min_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/min_hover.png
--------------------------------------------------------------------------------
/image/ButtonImage/min_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/min_normal.png
--------------------------------------------------------------------------------
/image/ButtonImage/minus_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/minus_hover.png
--------------------------------------------------------------------------------
/image/ButtonImage/minus_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/image/ButtonImage/minus_normal.png
--------------------------------------------------------------------------------
/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/main.qml
4 | qml/CusButton.qml
5 | qml/MoveMouseArea.qml
6 | qml/ResizeMouseArea.qml
7 | image/9.jpg
8 | image/ButtonImage/add_hover.png
9 | image/ButtonImage/add_normal.png
10 | image/ButtonImage/close_down.png
11 | image/ButtonImage/close_hover.png
12 | image/ButtonImage/close_normal.png
13 | image/ButtonImage/menu_down.png
14 | image/ButtonImage/menu_hover.png
15 | image/ButtonImage/menu_normal.png
16 | image/ButtonImage/min_down.png
17 | image/ButtonImage/min_hover.png
18 | image/ButtonImage/min_normal.png
19 | image/ButtonImage/minus_hover.png
20 | image/ButtonImage/minus_normal.png
21 |
22 |
23 |
--------------------------------------------------------------------------------
/qml/CusButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | Rectangle
4 | {
5 | id: root
6 | color: hovered ? "#9AFFFFFF" : "transparent";
7 |
8 | property string buttonNormalImage: "";
9 | property string buttonPressedImage: "";
10 | property string buttonHoverImage: "";
11 | property bool hovered: false;
12 |
13 | signal pressed();
14 | signal released();
15 | signal clicked();
16 | signal exited();
17 | signal entered();
18 |
19 | Image
20 | {
21 | id: image
22 | anchors.fill: parent
23 | fillMode: Image.PreserveAspectFit
24 | antialiasing: true
25 | mipmap: true
26 | source: root.buttonNormalImage
27 |
28 | MouseArea
29 | {
30 | anchors.fill: parent
31 | hoverEnabled: true
32 |
33 | onEntered:
34 | {
35 | root.entered();
36 | root.hovered = true;
37 | image.source = buttonHoverImage;
38 | }
39 | onPressed:
40 | {
41 | root.pressed();
42 | root.clicked();
43 | image.source = buttonPressedImage;
44 | }
45 | onReleased:
46 | {
47 | root.released();
48 | image.source = buttonNormalImage;
49 | }
50 | onExited:
51 | {
52 | root.exited();
53 | root.hovered = false;
54 | image.source = buttonNormalImage;
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/qml/MoveMouseArea.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | MouseArea
4 | {
5 | hoverEnabled: true
6 |
7 | property var target: undefined;
8 | property point startPoint: Qt.point(0, 0);
9 | property point offsetPoint: Qt.point(0, 0);
10 |
11 | onPressed:
12 | {
13 | cursorShape = Qt.SizeAllCursor;
14 | startPoint = Qt.point(mouseX, mouseY);
15 | }
16 | onPositionChanged:
17 | {
18 | if(pressed)
19 | {
20 | offsetPoint = Qt.point(mouse.x - startPoint.x, mouse.y - startPoint.y);
21 | target.x = target.x + offsetPoint.x;
22 | target.y = target.y + offsetPoint.y;
23 | }
24 | }
25 | onReleased:
26 | {
27 | cursorShape = Qt.ArrowCursor;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/qml/ResizeMouseArea.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.7
2 |
3 | /*
4 | ↑ ↑ ↑
5 | ←\1\ \2\ \3\→
6 | ←\4\ \5\ \6\→
7 | ←\7\ \8\ \9\→
8 | ↓ ↓ ↓
9 | 分8个缩放区域
10 | \5\为拖动区域
11 | target 缩放目标
12 | startPoint 鼠标起始点
13 | fixedPont 用于固定窗口的点
14 | 每一个area 大小 8 x 8
15 | */
16 |
17 | Item
18 | {
19 | id: area
20 |
21 | property var target: undefined;
22 | property point startPoint: Qt.point(0, 0);
23 | property point fixedPont: Qt.point(0, 0);
24 |
25 | MouseArea
26 | {
27 | id: area1
28 | x: 0
29 | y: 0
30 | width: 8
31 | height: 8
32 | hoverEnabled: true
33 |
34 | onEntered: cursorShape = Qt.SizeFDiagCursor;
35 | onExited: cursorShape = Qt.ArrowCursor;
36 | onPressed: startPoint = Qt.point(mouseX, mouseY);
37 | onPositionChanged:
38 | {
39 | if(pressed)
40 | {
41 | var offsetX = mouse.x - startPoint.x;
42 | var offsetY = mouse.y - startPoint.y;
43 | if ((target.width - offsetX) >= target.minimumWidth) //如果本次调整小于最小限制,则调整为最小
44 | {
45 | target.width -= offsetX;
46 | target.x += offsetX;
47 | }
48 | else
49 | {
50 | target.x += (target.width - target.minimumWidth);
51 | target.width -= (target.width - target.minimumWidth);
52 | }
53 |
54 | if ((target.height - offsetY) >= target.minimumHeight && offsetY != 0)
55 | {
56 | target.height -= offsetY;
57 | target.y += offsetY;
58 | }
59 | }
60 | }
61 | }
62 |
63 | MouseArea
64 | {
65 | id: area2
66 | x: 8
67 | y: 0
68 | width: target.width - 16
69 | height: 8
70 | hoverEnabled: true
71 |
72 | onEntered: cursorShape = Qt.SizeVerCursor;
73 | onExited: cursorShape = Qt.ArrowCursor;
74 | onPressed: startPoint = Qt.point(mouseX, mouseY);
75 | onPositionChanged:
76 | {
77 | if(pressed)
78 | {
79 | var offsetY = mouse.y - startPoint.y;
80 | if ((target.height - offsetY) >= target.minimumHeight && offsetY != 0)
81 | {
82 | target.height -= offsetY;
83 | target.y += offsetY;
84 | }
85 | }
86 | }
87 | }
88 |
89 | MouseArea
90 | {
91 | id: area3
92 | x: target.width - 8
93 | y: 0
94 | width: 8
95 | height: 8
96 | hoverEnabled: true
97 |
98 | onEntered: cursorShape = Qt.SizeBDiagCursor;
99 | onExited: cursorShape = Qt.ArrowCursor
100 | onPressed:
101 | {
102 | startPoint = Qt.point(mouseX, mouseY);
103 | fixedPont = Qt.point(target.x, target.y)
104 | }
105 | onPositionChanged:
106 | {
107 | if(pressed)
108 | {
109 | var offsetX = mouse.x - startPoint.x;
110 | var offsetY = mouse.y - startPoint.y;
111 | if ((target.width + offsetX) >= target.minimumWidth && offsetX != 0)
112 | {
113 | target.width += offsetX;
114 | target.x = fixedPont.x;
115 | }
116 | if ((target.height - offsetY) >= target.minimumHeight && offsetY != 0)
117 | {
118 | target.height -= offsetY;
119 | target.y += offsetY;
120 | }
121 | }
122 | }
123 | }
124 |
125 | MouseArea
126 | {
127 | id: area4
128 | x: 0
129 | y: 8
130 | width: 8
131 | height: target.height - 16
132 | hoverEnabled: true
133 |
134 | onEntered: cursorShape = Qt.SizeHorCursor;
135 | onExited: cursorShape = Qt.ArrowCursor;
136 | onPressed:
137 | {
138 | startPoint = Qt.point(mouseX, mouseY);
139 | }
140 | onPositionChanged:
141 | {
142 | if(pressed)
143 | {
144 | var offsetX = mouse.x - startPoint.x;
145 | if ((target.width - offsetX) >= target.minimumWidth)
146 | {
147 | target.width -= offsetX;
148 | target.x += offsetX;
149 | }
150 | }
151 | }
152 | }
153 |
154 | MoveMouseArea
155 | {
156 | id: area5
157 | x: 8
158 | y: 8
159 | width: area.target.width - 16
160 | height: area.target.height - 16
161 | target: area.target
162 | }
163 |
164 | MouseArea
165 | {
166 | id: area6
167 | x: target.width - 8
168 | y: 8
169 | width: 8
170 | height: target.height - 16
171 | hoverEnabled: true
172 | property real fixedX: 0;
173 |
174 | onEntered: cursorShape = Qt.SizeHorCursor;
175 | onExited: cursorShape = Qt.ArrowCursor;
176 | onPressed:
177 | {
178 | startPoint = Qt.point(mouseX, mouseY);
179 | fixedPont = Qt.point(target.x, target.y)
180 | }
181 | onPositionChanged:
182 | {
183 | if(pressed)
184 | {
185 | var offsetX = mouse.x - startPoint.x;
186 | if ((target.width + offsetX) >= target.minimumWidth && offsetX != 0)
187 | {
188 | target.width += offsetX;
189 | target.x = fixedPont.x;
190 | }
191 | }
192 | }
193 | }
194 |
195 | MouseArea
196 | {
197 | id: area7
198 | x: 0
199 | y: target.height - 8
200 | width: 8
201 | height: target.height - 16
202 | hoverEnabled: true
203 | property real fixedX: 0;
204 |
205 | onEntered: cursorShape = Qt.SizeBDiagCursor;
206 | onExited: cursorShape = Qt.ArrowCursor;
207 | onPressed:
208 | {
209 | startPoint = Qt.point(mouseX, mouseY);
210 | fixedPont = Qt.point(target.x, target.y)
211 | }
212 | onPositionChanged:
213 | {
214 | if (pressed)
215 | {
216 | var offsetX = mouse.x - startPoint.x;
217 | var offsetY = mouse.y - startPoint.y;
218 | if ((target.width - offsetX) >= target.minimumWidth && offsetX != 0)
219 | {
220 | target.width -= offsetX;
221 | target.x += offsetX;
222 | }
223 | if ((target.height + offsetY) >= target.minimumHeight && offsetY != 0)
224 | {
225 | target.height += offsetY;
226 | target.y = fixedPont.y;
227 | }
228 | }
229 | }
230 | }
231 |
232 | MouseArea
233 | {
234 | id: area8
235 | x: 8
236 | y: target.height - 8
237 | width: target.height - 16
238 | height: 8
239 | hoverEnabled: true
240 | property real fixedX: 0;
241 |
242 | onEntered: cursorShape = Qt.SizeVerCursor;
243 | onExited: cursorShape = Qt.ArrowCursor;
244 | onPressed:
245 | {
246 | startPoint = Qt.point(mouseX, mouseY);
247 | fixedPont = Qt.point(target.x, target.y)
248 | }
249 | onPositionChanged:
250 | {
251 | if (pressed)
252 | {
253 | var offsetY = mouse.y - startPoint.y;
254 | if ((target.height + offsetY) >= target.minimumHeight && offsetY != 0)
255 | {
256 | target.height += offsetY;
257 | target.y = fixedPont.y;
258 | }
259 | }
260 | }
261 | }
262 |
263 | MouseArea
264 | {
265 | id: area9
266 | x: target.width - 8
267 | y: target.height - 8
268 | width: 8
269 | height: 8
270 | hoverEnabled: true
271 |
272 | onEntered: cursorShape = Qt.SizeFDiagCursor;
273 | onExited: cursorShape = Qt.ArrowCursor
274 | onPressed:
275 | {
276 | startPoint = Qt.point(mouseX, mouseY);
277 | fixedPont = Qt.point(target.x, target.y)
278 | }
279 | onPositionChanged:
280 | {
281 | if(pressed)
282 | {
283 | var offsetX = mouse.x - startPoint.x;
284 | var offsetY = mouse.y - startPoint.y;
285 | if ((target.width + offsetX) >= target.minimumWidth && offsetX != 0)
286 | {
287 | target.width += offsetX;
288 | target.x = fixedPont.x;
289 | }
290 | if ((target.height + offsetY) >= target.minimumHeight && offsetY != 0)
291 | {
292 | target.height += offsetY;
293 | target.y = fixedPont.y;
294 | }
295 | }
296 | }
297 | }
298 | }
299 |
300 |
--------------------------------------------------------------------------------
/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.2
3 | import QtQuick.Controls 1.4
4 | import QtQuick.Controls 2.2
5 | import an.framelessWindow 1.0
6 |
7 | FramelessWindow
8 | {
9 | id: root
10 | visible: true
11 | width: 640
12 | height: 480
13 | x: (Screen.desktopAvailableWidth - actualWidth) / 2
14 | y: (Screen.desktopAvailableHeight - actualHeight) / 2
15 | actualHeight: height
16 | actualWidth: width
17 | title: qsTr("MPS Chat 服务器")
18 | taskbarHint: true
19 | windowIcon: "qrc:/image/winIcon.png"
20 |
21 | property var usersArray: new Array;
22 |
23 | function displayServerInfo(ip, port)
24 | {
25 | serverIP.text = "服务器 IP : " + ip;
26 | serverPort.text = "服务器 端口 : " + port;
27 | }
28 |
29 | function addMessage(msg)
30 | {
31 | messageText.text += "\n" + msg;
32 | }
33 |
34 | function retState(state)
35 | {
36 | switch (state)
37 | {
38 | case 0:
39 | return "在线";
40 | case 1:
41 | return "隐身";
42 | case 2:
43 | return "忙碌";
44 | case 3:
45 | return "离线";
46 | }
47 | }
48 |
49 | function addNewClient(username, ip, state)
50 | {
51 | usersArray.push(username);
52 | myModel.append({ "ip": ip,
53 | "username": username,
54 | "state": retState(state) });
55 | addMessage(username + "已经连接...");
56 | }
57 |
58 | function stateChange(username, state)
59 | {
60 | var index = usersArray.indexOf(username);
61 | if (index != -1)
62 | {
63 | myModel.setProperty(index, "state", retState(state));
64 | }
65 | }
66 |
67 | function removeClient(username)
68 | {
69 | var index = usersArray.indexOf(username);
70 | if (index != -1)
71 | {
72 | usersArray.splice(index, 1);
73 | myModel.remove(index);
74 | }
75 | }
76 |
77 | /*Image
78 | {
79 | id: background
80 | source: "qrc:/image/9.jpg"
81 | }*/
82 |
83 | Rectangle
84 | {
85 | x: 0
86 | y: 0
87 | width: parent.width
88 | height: parent.height
89 | radius: consoleWindow.radius
90 |
91 | Rectangle
92 | {
93 | anchors.fill: parent
94 | radius: parent.radius
95 | gradient: Gradient
96 | {
97 | GradientStop
98 | {
99 | position: 0.000
100 | color: "#88BBEEFA"
101 | }
102 | GradientStop
103 | {
104 | position: 0.500
105 | color: "#8800EA75"
106 | }
107 | GradientStop
108 | {
109 | position: 1.000
110 | color: "#88BBEEFA"
111 | }
112 | }
113 | }
114 |
115 | ResizeMouseArea
116 | {
117 | id: resizeMouseArea
118 | target: root
119 | }
120 |
121 | Row
122 | {
123 | width: 102
124 | height: 40
125 | anchors.right: parent.right
126 | anchors.rightMargin: 6
127 | anchors.top: parent.top
128 | anchors.topMargin: 6
129 |
130 | CusButton
131 | {
132 | id: menuButton
133 | width: 32
134 | height: 32
135 |
136 | onClicked:
137 | {
138 | }
139 | Component.onCompleted:
140 | {
141 | buttonNormalImage = "qrc:/image/ButtonImage/menu_normal.png";
142 | buttonPressedImage = "qrc:/image/ButtonImage/menu_down.png";
143 | buttonHoverImage = "qrc:/image/ButtonImage/menu_hover.png";
144 | }
145 | }
146 |
147 | CusButton
148 | {
149 | id: minButton
150 | width: 32
151 | height: 32
152 |
153 | onClicked:
154 | {
155 | root.showMinimized();
156 | }
157 | Component.onCompleted:
158 | {
159 | buttonNormalImage = "qrc:/image/ButtonImage/min_normal.png";
160 | buttonPressedImage = "qrc:/image/ButtonImage/min_down.png";
161 | buttonHoverImage = "qrc:/image/ButtonImage/min_hover.png";
162 | }
163 | }
164 |
165 | CusButton
166 | {
167 | id: closeButton
168 | width: 32
169 | height: 32
170 |
171 | onClicked:
172 | {
173 | root.close();
174 | }
175 | Component.onCompleted:
176 | {
177 | buttonNormalImage = "qrc:/image/ButtonImage/close_normal.png";
178 | buttonPressedImage = "qrc:/image/ButtonImage/close_down.png";
179 | buttonHoverImage = "qrc:/image/ButtonImage/close_hover.png";
180 | }
181 | }
182 | }
183 |
184 | ListModel
185 | {
186 | id: myModel
187 | }
188 |
189 | Row
190 | {
191 | id: serverRect
192 | anchors.horizontalCenter: parent.horizontalCenter
193 | width: 400
194 | height: 45
195 |
196 | Text
197 | {
198 | id: serverIP
199 | height: 45
200 | width: 200
201 | color: "red"
202 | verticalAlignment: Text.AlignVCenter
203 | horizontalAlignment: Text.AlignHCenter
204 | font.family: "微软雅黑"
205 | font.pointSize: 11
206 | }
207 |
208 | Text
209 | {
210 | id: serverPort
211 | height: 45
212 | width: 200
213 | color: "red"
214 | verticalAlignment: Text.AlignVCenter
215 | horizontalAlignment: Text.AlignHCenter
216 | font.family: "微软雅黑"
217 | font.pointSize: 11
218 | }
219 | }
220 |
221 | TableView
222 | {
223 | id: tableView
224 | anchors.left: parent.left
225 | anchors.right: parent.right
226 | anchors.top: serverRect.bottom
227 | anchors.bottom: consoleWindow.top
228 | anchors.bottomMargin: 20
229 | model: myModel
230 | backgroundVisible: false
231 | headerDelegate: Component
232 | {
233 | id: headerDelegate
234 |
235 | Rectangle
236 | {
237 | id: headerRect;
238 | height: 30
239 | width: 100
240 | border.color: "#400040"
241 | color: styleData.selected ? "gray" : "transparent"
242 | radius: 3
243 |
244 | Text
245 | {
246 | text: styleData.value
247 | anchors.centerIn: parent
248 | font.family: "微软雅黑"
249 | font.pointSize: 10
250 | color: "red"
251 | }
252 | }
253 | }
254 | rowDelegate: Component
255 | {
256 | id: rowDelegate
257 | Rectangle
258 | {
259 | color: "transparent"
260 | height: 42
261 | }
262 | }
263 | itemDelegate: Component
264 | {
265 | id: itemDelegate
266 |
267 | Rectangle
268 | {
269 | id: tableCell
270 | anchors.fill: parent
271 | anchors.topMargin: 4
272 | anchors.leftMargin: 1
273 | border.color: "#400040"
274 | radius: 3
275 | color: styleData.selected ? "#44EEEEEE" : "#66B5E61D"
276 |
277 | Text
278 | {
279 | id: textID
280 | text: styleData.value
281 | font.family: "微软雅黑"
282 | anchors.fill: parent
283 | color: "black"
284 | elide: Text.ElideRight
285 | verticalAlignment: Text.AlignVCenter
286 | horizontalAlignment: Text.AlignHCenter
287 | }
288 | }
289 | }
290 |
291 |
292 | TableViewColumn
293 | {
294 | role: "username"
295 | title: "帐号"
296 | width: 180
297 | }
298 |
299 | TableViewColumn
300 | {
301 | role: "ip"
302 | title: "连接IP"
303 | width: 180
304 | }
305 |
306 | TableViewColumn
307 | {
308 | role: "state"
309 | title: "状态"
310 | width: 100
311 | }
312 | }
313 |
314 | Rectangle
315 | {
316 | id: consoleWindow
317 | opacity: 0.9
318 | radius: 10
319 | clip: true
320 | height: 160
321 | anchors.left: parent.left
322 | anchors.right: parent.right
323 | anchors.bottom: parent.bottom
324 |
325 | Flickable
326 | {
327 | id: flick
328 | //interactive: false
329 | anchors.fill: parent
330 | anchors.margins: 15
331 | contentHeight: messageText.contentHeight
332 | contentWidth: messageText.contentWidth
333 |
334 | TextEdit
335 | {
336 | id: messageText
337 | width: flick.width
338 | height: flick.height
339 | wrapMode: Text.WrapAnywhere
340 | font.family: "微软雅黑"
341 | //textFormat: Text.RichText
342 | text: "服务器运行中..."
343 | color: "#400040"
344 | onTextChanged: flick.contentY = Math.max(0, contentHeight - height);
345 | }
346 |
347 | ScrollBar.vertical: ScrollBar
348 | {
349 | width: 12
350 | policy: ScrollBar.AlwaysOn
351 | }
352 | }
353 | }
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/src/chatsocket.cpp:
--------------------------------------------------------------------------------
1 | #include "chatsocket.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | ChatSocket::ChatSocket(qintptr socketDescriptor, QObject *parent)
12 | : QTcpSocket(parent)
13 | , m_status(Offline)
14 | {
15 | if (!setSocketDescriptor(socketDescriptor))
16 | {
17 | emit logMessage(errorString());
18 | deleteLater();
19 | }
20 |
21 | m_username = CLIENT_ID;
22 | m_heartbeat = new QTimer(this);
23 | m_heartbeat->setInterval(60000);
24 | m_sendDataBytes = 0;
25 | m_sendData = QByteArray();
26 | m_recvData = QByteArray();
27 | m_lastTime = QDateTime::currentDateTime();
28 |
29 | connect(this, &ChatSocket::readyRead, this, &ChatSocket::heartbeat);
30 | connect(this, &ChatSocket::bytesWritten, this, &ChatSocket::continueWrite, Qt::DirectConnection);
31 | connect(this, &ChatSocket::readyRead, this, [this]()
32 | {
33 | m_recvData += readAll();
34 | processRecvMessage();
35 | });
36 | connect(this, &ChatSocket::disconnected, this, &ChatSocket::onDisconnected);
37 | connect(m_heartbeat, &QTimer::timeout, this, &ChatSocket::checkHeartbeat);
38 |
39 | m_heartbeat->start(); //开始心跳
40 | }
41 |
42 | ChatSocket::~ChatSocket()
43 | {
44 |
45 | }
46 |
47 | void ChatSocket::heartbeat()
48 | {
49 | if (!m_heartbeat->isActive())
50 | m_heartbeat->start();
51 | m_lastTime = QDateTime::currentDateTime();
52 | }
53 |
54 | void ChatSocket::continueWrite(qint64 sentSize)
55 | {
56 | static int sentBytes = 0;
57 | sentBytes += sentSize;
58 |
59 | if (sentBytes >= m_sendDataBytes)
60 | {
61 | m_sendDataBytes = 0;
62 | sentBytes = 0;
63 | m_sendData.clear();
64 | m_hasMessageProcessing = false;
65 | if (!m_messageQueue.isEmpty())
66 | processNextSendMessage(); //如果消息队列不为空,则继续处理下一条待发送消息
67 | }
68 | }
69 |
70 | void ChatSocket::checkHeartbeat()
71 | {
72 | if (m_lastTime.secsTo(QDateTime::currentDateTime()) >= 60) //检测掉线,停止心跳
73 | {
74 | qDebug() << "heartbeat 超时, 即将断开连接";
75 | m_heartbeat->stop();
76 | disconnectFromHost();
77 | }
78 | }
79 |
80 | void ChatSocket::onDisconnected()
81 | {
82 | emit clientDisconnected(m_username);
83 | emit logMessage(peerAddress().toString() + " 断开连接..");
84 | deleteLater();
85 | }
86 |
87 | void ChatSocket::processNextSendMessage()
88 | {
89 | if (!m_hasMessageProcessing && !m_messageQueue.isEmpty())
90 | {
91 | QByteArray block;
92 | QDataStream out(&block, QIODevice::WriteOnly);
93 | out.setVersion(QDataStream::Qt_5_9);
94 | Message *message = m_messageQueue.dequeue();
95 | out << *message;
96 | m_sendData = block;
97 | m_sendDataBytes = block.size();
98 | m_hasMessageProcessing = true;
99 | delete message;
100 |
101 | write(block);
102 | flush(); //立即发送消息
103 | }
104 | }
105 |
106 | void ChatSocket::toJsonAndSend(const UserInfo &info, const QMap > &friends)
107 | {
108 | QJsonObject object;
109 | object.insert("Username", info.username);
110 | object.insert("Nickname", info.nickname);
111 | object.insert("HeadImage", info.headImage);
112 | object.insert("Background", info.background);
113 | object.insert("Gender", info.gender);
114 | object.insert("Birthday", info.birthday);
115 | object.insert("Signature", info.signature);
116 | object.insert("UnreadMessage", 0);
117 | object.insert("Level", info.level);
118 | QJsonArray friendList;
119 | for (auto it = friends.constBegin(); it != friends.constEnd(); it++)
120 | {
121 | QJsonArray array;
122 | for (auto it2 : it.value())
123 | {
124 | UserInfo info2 = m_database->getUserInfo(it2.friendname);
125 | QJsonObject object2;
126 | object2.insert("Username", info2.username);
127 | object2.insert("Nickname", info2.nickname);
128 | object2.insert("HeadImage", info2.headImage);
129 | object2.insert("Gender", info2.gender);
130 | object2.insert("Birthday", info2.birthday);
131 | object2.insert("Signature", info2.signature);
132 | object2.insert("UnreadMessage", it2.unreadMessage);
133 | object2.insert("Level", info2.level);
134 | array.append(object2);
135 | }
136 | QJsonObject obj;
137 | obj.insert("Group", it.key());
138 | obj.insert("Friend", array);
139 | friendList.append(obj);
140 | }
141 | object.insert("FriendList", friendList);
142 | writeClientData(SERVER_ID, MT_USERINFO, MO_NULL, QJsonDocument(object).toJson());
143 | qDebug() << object;
144 | }
145 |
146 | void ChatSocket::updateInfomation(const QByteArray &infoJson)
147 | {
148 | UserInfo info = jsonToInfo(infoJson);
149 | qDebug() << info;
150 | m_database->setUserInfo(info);
151 | }
152 |
153 | UserInfo ChatSocket::jsonToInfo(const QByteArray &json)
154 | {
155 | UserInfo info;
156 | QJsonParseError error;
157 | QJsonDocument doc = QJsonDocument::fromJson(json, &error);
158 | if (!doc.isNull() && (error.error == QJsonParseError::NoError))
159 | {
160 | if (doc.isObject())
161 | {
162 | QJsonObject object = doc.object();
163 | QJsonValue value = object.value("Username");
164 | if (value.isString())
165 | info.username = value.toString();
166 | value = object.value("Password");
167 | if (value.isString())
168 | info.password = value.toString();
169 | value = object.value("Nickname");
170 | if (value.isString())
171 | info.nickname = value.toString();
172 | value = object.value("HeadImage");
173 | if (value.isString())
174 | info.headImage = value.toString();
175 | value = object.value("Background");
176 | if (value.isString())
177 | info.background = value.toString();
178 | value = object.value("Gender");
179 | if (value.isString())
180 | info.gender = value.toString();
181 | value = object.value("Signature");
182 | if (value.isString())
183 | info.signature = value.toString();
184 | value = object.value("Birthday");
185 | if (value.isString())
186 | info.birthday = value.toString();
187 | value = object.value("Level");
188 | if (value.isDouble())
189 | info.level = value.toInt();
190 | }
191 | }
192 | else qDebug() << error.errorString();
193 |
194 | return info;
195 | }
196 |
197 | QByteArray ChatSocket::infoToJson(const UserInfo &info)
198 | {
199 | QJsonObject object;
200 | object.insert("Username", info.username);
201 | object.insert("Nickname", info.nickname);
202 | object.insert("HeadImage", info.headImage);
203 | object.insert("Gender", info.gender);
204 | object.insert("Birthday", info.birthday);
205 | object.insert("Signature", info.signature);
206 | object.insert("Level", info.level);
207 |
208 | return QJsonDocument(object).toJson();
209 | }
210 |
211 | void ChatSocket::writeClientData(const QByteArray &sender, msg_t type, msg_option_t option, const QByteArray &data)
212 | {
213 | QByteArray base64 = data.toBase64();
214 | QByteArray md5 = QCryptographicHash::hash(base64, QCryptographicHash::Md5);
215 |
216 | MessageHeader header = { MSG_FLAG, type, msg_size_t(base64.size()), option, sender, m_username, md5 };
217 | Message *message = new Message(header, base64);
218 | m_messageQueue.enqueue(message);
219 | processNextSendMessage();
220 | }
221 |
222 | void ChatSocket::processRecvMessage()
223 | {
224 | //尝试读取一个完整的消息头
225 | if (m_recvHeader.isEmpty() && m_recvData.size() > 0)
226 | {
227 | MessageHeader header;
228 | QDataStream in(&m_recvData, QIODevice::ReadOnly);
229 | in.setVersion(QDataStream::Qt_5_9);
230 | in >> header;
231 |
232 | if (header.isEmpty()) return;
233 | qDebug() << header;
234 | m_recvHeader = header;
235 | m_recvData.remove(0, header.getSize() + 4); //data为QByteArray,前面有4字节的大小
236 |
237 | //如果成功读取了一个完整的消息头,但flag不一致(即:不是我的消息)
238 | if (get_flag(m_recvHeader) != MSG_FLAG)
239 | {
240 | m_recvHeader = MessageHeader();
241 | return;
242 | }
243 | }
244 |
245 | //如果数据大小不足一条消息
246 | int size = int(get_size(m_recvHeader));
247 | if (m_recvData.size() < size)
248 | return;
249 |
250 | auto rawData = m_recvData.left(size);
251 | m_recvData = m_recvData.mid(size);
252 |
253 | auto md5 = QCryptographicHash::hash(rawData, QCryptographicHash::Md5);
254 | auto data = QByteArray::fromBase64(rawData);
255 | if (md5 != get_md5(m_recvHeader)) return;
256 |
257 | qDebug() << "md5 一致,消息为:" + data;
258 | qDebug() << "消息大小:" + QString::number(data.size());
259 |
260 | switch (get_type(m_recvHeader))
261 | {
262 | case MT_HEARTBEAT:
263 | break;
264 | case MT_CHECK:
265 | {
266 | QString str = QString::fromLocal8Bit(data);
267 | QStringList list = str.split("%%");
268 | qDebug() << "登录信息:" << list;
269 | m_username = list.at(0).toLatin1(); //记录该socket的帐号
270 | m_database = new Database(m_username + QString::number(qintptr(QThread::currentThreadId()), 16), this);
271 | UserInfo info = m_database->getUserInfo(list.at(0));
272 | if (info.password == list.at(1))
273 | {
274 | writeClientData(SERVER_ID, MT_CHECK, MO_NULL, CHECK_SUCCESS);
275 | emit clientLoginSuccess(m_username, peerAddress().toString());
276 | }
277 | else
278 | {
279 | writeClientData(SERVER_ID, MT_CHECK, MO_NULL, CHECK_FAILURE);
280 | }
281 | break;
282 | }
283 | case MT_USERINFO:
284 | {
285 | qDebug() << "MT_USERINFO" << get_option(m_recvHeader);
286 | if (get_option(m_recvHeader) == MO_DOWNLOAD)
287 | {
288 | auto info = m_database->getUserInfo(m_username);
289 | auto friendsInfo = m_database->getUserFriendsInfo(m_username);
290 | toJsonAndSend(info, friendsInfo);
291 | }
292 | else if (get_option(m_recvHeader) == MO_UPLOAD)
293 | {
294 | updateInfomation(data);
295 | }
296 | break;
297 | }
298 | case MT_STATECHANGE:
299 | {
300 | m_status = ChatStatus(data.toInt());
301 | emit hasNewMessage(m_username, get_receiver(m_recvHeader), MT_STATECHANGE, MO_NULL, data);
302 | break;
303 | }
304 | case MT_SEARCH:
305 | {
306 | QString username = QString::fromLocal8Bit(data);
307 | qDebug() << "获取" << username << "的信息";
308 | UserInfo info = m_database->getUserInfo(username);
309 |
310 | QByteArray sendData = infoToJson(info);
311 | writeClientData(SERVER_ID, MT_SEARCH, MO_NULL, sendData);
312 | break;
313 | }
314 | case MT_SHAKE:
315 | {
316 | QString str = QString::fromLocal8Bit(data);
317 | qDebug() << "发送给" << get_receiver(m_recvHeader) << str;
318 | emit hasNewMessage(m_username, get_receiver(m_recvHeader), MT_SHAKE, get_option(m_recvHeader), data);
319 | break;
320 | }
321 | case MT_TEXT:
322 | {
323 | QString str = QString::fromLocal8Bit(data);
324 | qDebug() << "发送给" << get_receiver(m_recvHeader) << "的消息:" << str;
325 | emit hasNewMessage(m_username, get_receiver(m_recvHeader), MT_TEXT, get_option(m_recvHeader), data);
326 | break;
327 | }
328 |
329 | case MT_IMAGE:
330 | break;
331 |
332 | case MT_FILE:
333 | break;
334 |
335 | case MT_ADDFRIEND:
336 | {
337 | qDebug() << "发送给" << get_receiver(m_recvHeader) << "的添加好友请求。";
338 | QString addStr = QString::fromLocal8Bit(data);
339 | if (addStr == ADD_SUCCESS) //添加好友成功
340 | {
341 | m_database->addFriend(get_sender(m_recvHeader), get_receiver(m_recvHeader));
342 | //将好友信息发送回去
343 | data = infoToJson(m_database->getUserInfo(get_receiver(m_recvHeader)));
344 | writeClientData(get_receiver(m_recvHeader), MT_ADDFRIEND, MO_NULL, data);
345 | //发送给另一个好友
346 | data = infoToJson(m_database->getUserInfo(get_sender(m_recvHeader)));
347 | }
348 | emit hasNewMessage(m_username, get_receiver(m_recvHeader), MT_ADDFRIEND, MO_NULL, data);
349 | break;
350 | }
351 |
352 | case MT_REGISTER:
353 | {
354 | qDebug() << "来自" << get_sender(m_recvHeader) << "的注册请求。";
355 | UserInfo info = jsonToInfo(data);
356 | m_database = new Database(m_username + QString::number(qintptr(QThread::currentThreadId()), 16), this);
357 | if (!info.isEmpty())
358 | {
359 | if (m_database->createUser(info))
360 | writeClientData(SERVER_ID, MT_REGISTER, MO_NULL, REG_SUCCESS);
361 | else writeClientData(SERVER_ID, MT_REGISTER, MO_NULL, REG_FAILURE);
362 | }
363 | break;
364 | }
365 |
366 | case MT_UNKNOW:
367 | break;
368 |
369 | default:
370 | break;
371 | }
372 | //处理结束,清空消息头
373 | m_recvHeader = MessageHeader();
374 | }
375 |
--------------------------------------------------------------------------------
/src/chatsocket.h:
--------------------------------------------------------------------------------
1 | #ifndef CHATSOCKET_H
2 | #define CHATSOCKET_H
3 |
4 | #include "database.h"
5 | #include "protocol.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | class QTimer;
12 | class ChatSocket : public QTcpSocket
13 | {
14 | Q_OBJECT
15 |
16 | public:
17 | enum ChatStatus
18 | {
19 | Online = 0, //在线
20 | Stealth, //隐身
21 | Busy, //忙碌
22 | Offline //离线
23 | };
24 |
25 | public:
26 | ChatSocket(qintptr socketDescriptor, QObject *parent = nullptr);
27 | ~ChatSocket();
28 |
29 | int status() const { return m_status; }
30 |
31 | signals:
32 | void clientLoginSuccess(const QString &username, const QString &ip);
33 | void clientDisconnected(const QString &username);
34 | void hasNewMessage(const QByteArray &sender, const QByteArray &receiver,
35 | msg_t type, msg_option_t option, const QByteArray &data);
36 | void logMessage(const QString &message);
37 |
38 | public slots:
39 | void writeClientData(const QByteArray &sender, msg_t type, msg_option_t option,
40 | const QByteArray &data);
41 |
42 | private slots:
43 | void heartbeat();
44 | void continueWrite(qint64 sentSize);
45 | void checkHeartbeat();
46 | void onDisconnected();
47 | void processNextSendMessage();
48 | void processRecvMessage();
49 |
50 | private:
51 | //将查询到的数据转换成JSON并发送回客户端
52 | void toJsonAndSend(const UserInfo &info, const QMap > &friends);
53 | //将json转换成info并更新数据库
54 | void updateInfomation(const QByteArray &infoJson);
55 | //将json装换成info
56 | UserInfo jsonToInfo(const QByteArray &json);
57 | //将info装换成json
58 | QByteArray infoToJson(const UserInfo &info);
59 |
60 | private:
61 | ChatStatus m_status;
62 | QTimer *m_heartbeat;
63 | QDateTime m_lastTime;
64 | qint64 m_sendDataBytes;
65 | QByteArray m_sendData;
66 | QByteArray m_recvData;
67 | MessageHeader m_recvHeader;
68 |
69 | QByteArray m_username;
70 | QQueue m_messageQueue;
71 | bool m_hasMessageProcessing; //指示是否有消息在处理中
72 | Database *m_database;
73 | };
74 |
75 | #endif // CHATSOCKET_H
76 |
--------------------------------------------------------------------------------
/src/chattcpserver.cpp:
--------------------------------------------------------------------------------
1 | #include "chattcpserver.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | ChatTcpServer::ChatTcpServer(QQmlEngine *engine, QObject *parent)
9 | : QTcpServer(parent)
10 | , m_qmlengine(engine)
11 | {
12 | m_database = new Database("ServerConnection", this);
13 | }
14 |
15 | ChatTcpServer::~ChatTcpServer()
16 | {
17 |
18 | }
19 |
20 | void ChatTcpServer::loadWindow()
21 | {
22 | QQmlComponent component(m_qmlengine, "qrc:/qml/main.qml");
23 | QObject *obj = component.create();
24 | m_window = qobject_cast(obj);
25 | m_window->requestActivate();
26 | m_window->show();
27 |
28 | QMetaObject::invokeMethod(m_window, "displayServerInfo",
29 | Q_ARG(QVariant, QVariant(server_ip)), Q_ARG(QVariant, QVariant(43800)));
30 | }
31 |
32 | void ChatTcpServer::incomingConnection(qintptr socketDescriptor)
33 | {
34 | QThread *thread = new QThread;
35 | ChatSocket *socket = new ChatSocket(socketDescriptor);
36 |
37 | connect(thread, &QThread::finished, thread, &QThread::deleteLater);
38 | connect(socket, &ChatSocket::hasNewMessage, this, &ChatTcpServer::disposeMessage);
39 | connect(socket, &ChatSocket::logMessage, this, [this](const QString &message)
40 | {
41 | QMetaObject::invokeMethod(m_window, "addMessage", Q_ARG(QVariant, QVariant(message)));
42 | });
43 | connect(socket, &ChatSocket::clientLoginSuccess, this, [this](const QString &username, const QString &ip)
44 | {
45 | ChatSocket *socket = qobject_cast(sender());
46 | m_users[username] = socket;
47 | QMetaObject::invokeMethod(m_window, "addNewClient", Q_ARG(QVariant, username), Q_ARG(QVariant, ip),
48 | Q_ARG(QVariant, socket->status()));
49 | });
50 | connect(socket, &ChatSocket::clientDisconnected, this, [this](const QString &username)
51 | {
52 | m_users.remove(username);
53 | QMetaObject::invokeMethod(m_window, "removeClient", Q_ARG(QVariant, QVariant(username)));
54 | qDebug() << m_users;
55 | });
56 |
57 | socket->moveToThread(thread);
58 | thread->start();
59 | }
60 |
61 | void ChatTcpServer::saveRecord(const QByteArray &sender, const QByteArray &receiver, const QByteArray &data)
62 | {
63 | //以后会设计一个专门格式化存储聊天记录的工具
64 | QFile file("users/" + QString(sender) + "/messageText/MSG" + QString(receiver) + ".txt");
65 | file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
66 | QTextStream out(&file);
67 | out << "[time:" << QDateTime::currentDateTime().toString("yyyyMMdd hhmmss") << "]" << endl
68 | << "[data:" << QString::fromLocal8Bit(data) << "]" << endl;
69 | file.close();
70 |
71 | file.setFileName("users/" + QString(receiver) + "/messageText/MSG" + QString(sender) + ".txt");
72 | file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
73 | out << "[time:" << QDateTime::currentDateTime().toString("yyyyMMdd hhmmss") << "]" << endl
74 | << "[data:" << QString::fromLocal8Bit(data) << "]" << endl;
75 | file.close();
76 | }
77 |
78 | void ChatTcpServer::writeDataToClient(const QByteArray &sender, const QByteArray &receiver, msg_t type, const QByteArray &data)
79 | {
80 | //因为在不同的线程中,使用invokeMethod调用
81 | //此函数简化了操作
82 | QMetaObject::invokeMethod(m_users[QString(receiver)], "writeClientData", Q_ARG(QByteArray, sender),
83 | Q_ARG(msg_t, type), Q_ARG(msg_option_t, MO_NULL), Q_ARG(QByteArray, data));
84 | }
85 |
86 | void ChatTcpServer::disposeMessage(const QByteArray &sender, const QByteArray &receiver, msg_t type, msg_option_t option, const QByteArray &data)
87 | {
88 | switch (type)
89 | {
90 | case MT_SHAKE:
91 | case MT_TEXT:
92 | {
93 | //将双方的消息存入
94 | saveRecord(sender, receiver, data);
95 | if (m_users.contains(QString(receiver))) //如果另一方在线
96 | writeDataToClient(sender, receiver, type, data);
97 | else m_database->addUnreadMessage(QString(sender), QString(receiver)); //不在线则unreadMessage+1,下次登录时发送
98 | break;
99 | }
100 | case MT_STATECHANGE:
101 | {
102 | QMetaObject::invokeMethod(m_window, "stateChange", Q_ARG(QVariant, QString(sender)), Q_ARG(QVariant, m_users[sender]->status()));
103 | QStringList friends = m_database->getUserFriends(sender);
104 | for (auto it : friends)
105 | {
106 | if (m_users.contains(it)) //如果好友在线,则为其发送状态更新
107 | writeDataToClient(sender, it.toLatin1(), MT_STATECHANGE, data);
108 | }
109 | break;
110 | }
111 | case MT_ADDFRIEND:
112 | {
113 | if (m_users.contains(QString(receiver))) //如果另一方在线
114 | {
115 | writeDataToClient(sender, receiver, MT_ADDFRIEND, data);
116 | }
117 | }
118 |
119 | default:
120 | break;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/chattcpserver.h:
--------------------------------------------------------------------------------
1 | #ifndef CHATTCPSERVER_H
2 | #define CHATTCPSERVER_H
3 |
4 | #include "chatsocket.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class ChatSocket;
11 | class QQmlEngine;
12 | class QQuickWindow;
13 | class ChatTcpServer : public QTcpServer
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | ChatTcpServer(QQmlEngine *engine, QObject *parent = nullptr);
19 | ~ChatTcpServer() override;
20 |
21 | void loadWindow();
22 |
23 | protected:
24 | void incomingConnection(qintptr socketDescriptor) override;
25 |
26 | private slots:
27 | //保存聊天记录
28 | void saveRecord(const QByteArray &sender, const QByteArray &receiver, const QByteArray &data);
29 | //写入数据到指定客户端连接
30 | void writeDataToClient(const QByteArray &sender, const QByteArray &receiver, msg_t type, const QByteArray &data);
31 | //处理消息
32 | void disposeMessage(const QByteArray &sender, const QByteArray &receiver, msg_t type, msg_option_t option, const QByteArray &data);
33 |
34 | private:
35 | Database *m_database;
36 | QQmlEngine *m_qmlengine;
37 | QPointer m_window;
38 | QMap m_users;
39 | };
40 |
41 | #endif // CHATTCPSERVER_H
42 |
--------------------------------------------------------------------------------
/src/database.cpp:
--------------------------------------------------------------------------------
1 | #include "database.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | extern QDebug operator<<(QDebug debug, const UserInfo &info)
8 | {
9 | QDebugStateSaver saver(debug);
10 | debug << "username: " << info.username << "password: " << info.password << "nickname: " << info.nickname
11 | << "headImage: " << info.headImage << "background" << info.background << "gende: " << info.gender
12 | << "birthday: " << info.birthday << "signature: " << info.signature << "level: " << info.level;
13 | return debug;
14 | }
15 |
16 | Database::Database(const QString &connectionName, QObject *parent)
17 | : QObject(parent)
18 | , m_connectionName(connectionName)
19 | {
20 | QMutexLocker locker(&m_mutex);
21 | m_database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
22 | m_database.setDatabaseName("users/users.db");
23 | m_database.setHostName("localhost");
24 | m_database.setUserName("MChatServer");
25 | m_database.setPassword("123456");
26 | m_database.open();
27 | }
28 |
29 | Database::~Database()
30 | {
31 | closeDatabase();
32 | //QSqlDatabase::removeDatabase(m_connectionName);
33 | }
34 |
35 | bool Database::openDatabase()
36 | {
37 | if (!m_database.isOpen())
38 | {
39 | QMutexLocker locker(&m_mutex);
40 | return m_database.open();
41 | }
42 |
43 | return true;
44 | }
45 |
46 | void Database::closeDatabase()
47 | {
48 | if (m_database.isOpen())
49 | m_database.close();
50 | }
51 |
52 | bool Database::createUser(const UserInfo &info)
53 | {
54 | if (!tableExists())
55 | return false;
56 |
57 | QString user_dir = "users/" + info.username;
58 | if (!QFile::exists(user_dir)) //如果该用户不存在,则创建一系列文件夹
59 | {
60 | QDir dir;
61 | dir.mkpath(user_dir);
62 | dir.mkpath(user_dir + "/headImage");
63 | dir.mkpath(user_dir + "/messageImage");
64 | dir.mkpath(user_dir + "/messageText");
65 | }
66 |
67 | QString insert = "INSERT INTO info VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?);";
68 | QSqlQuery query(m_database);
69 | query.prepare(insert);
70 | query.addBindValue(info.username);
71 | query.addBindValue(info.password);
72 | query.addBindValue(info.nickname);
73 | query.addBindValue(info.headImage);
74 | query.addBindValue(info.background);
75 | query.addBindValue(info.gender);
76 | query.addBindValue(info.birthday);
77 | query.addBindValue(info.signature);
78 | query.addBindValue(info.level);
79 |
80 | if (query.exec())
81 | {
82 | return true;
83 | }
84 | else
85 | {
86 | qDebug() << __func__ << query.lastError().text();
87 | closeDatabase();
88 | return false;
89 | }
90 | }
91 |
92 | UserInfo Database::getUserInfo(const QString &username)
93 | {
94 | if (!tableExists())
95 | return UserInfo();
96 |
97 | QSqlQuery query(m_database);
98 | if (!query.exec("SELECT * FROM info WHERE user_username = '" + username + "';"))
99 | qDebug() << __func__ << query.lastError().text();
100 | query.next();
101 |
102 | UserInfo info;
103 | if (query.isValid())
104 | {
105 | info.username = query.value(0).toString();
106 | info.password = query.value(1).toString();
107 | info.nickname = query.value(2).toString();
108 | info.headImage = query.value(3).toString();
109 | info.background = query.value(4).toString();
110 | info.gender = query.value(5).toString();
111 | info.birthday = query.value(6).toString();
112 | info.signature = query.value(7).toString();
113 | info.level = query.value(8).toInt();
114 | }
115 | else qDebug() << __func__ << "未找到" + username + "的信息。";
116 |
117 | return info;
118 | }
119 |
120 | bool Database::addFriend(const QString &username, const QString &friendname)
121 | {
122 | /*if (username > friendname)
123 | qSwap(username, friendname);*/
124 | QString insert = "INSERT INTO users VALUES(?, ?, ?, ?, ?, ?);";
125 | QSqlQuery query(m_database);
126 | query.prepare(insert);
127 | query.addBindValue(username);
128 | query.addBindValue(friendname);
129 | query.addBindValue("我的好友");
130 | query.addBindValue("我的好友");
131 | query.addBindValue(0);
132 | query.addBindValue(0);
133 |
134 | if (query.exec())
135 | {
136 | return true;
137 | }
138 | else
139 | {
140 | qDebug() << __func__ << query.lastError().text();;
141 | closeDatabase();
142 | return false;
143 | }
144 | }
145 |
146 | void Database::setUserInfo(const UserInfo &info)
147 | {
148 | QString query_update = "UPDATE info "
149 | "SET user_password = '" + info.password +
150 | "', user_nickname = '" + info.nickname +
151 | "', user_headImage = '" + info.headImage +
152 | "', user_background = '" + info.background +
153 | "', user_gender = '" + info.gender +
154 | "', user_birthday = '" + info.birthday +
155 | "', user_signature = '" + info.signature +
156 | "', user_level = " + QString::number(info.level) +
157 | " WHERE user_username = '" + info.username + "';";
158 | QSqlQuery query(m_database);
159 | if (query.exec(query_update))
160 | {
161 | qDebug() << "user info 更新成功";
162 | }
163 | else
164 | {
165 | qDebug() << __func__ << query.lastError().text();
166 | closeDatabase();
167 | }
168 | }
169 |
170 | QStringList Database::getUserFriends(const QString &username)
171 | {
172 | QString query_friends = "SELECT user_friend AS user_friend "
173 | "FROM users "
174 | "WHERE user_username = '" + username + "' "
175 | "UNION ALL "
176 | "SELECT user_username AS user_friend "
177 | "FROM users "
178 | "WHERE user_friend = '" + username + "'; ";
179 |
180 | QSqlQuery query(m_database);
181 | QStringList friends;
182 | if (query.exec(query_friends))
183 | {
184 | while (query.next())
185 | {
186 | if (query.isValid())
187 | friends << query.value(0).toString();
188 | }
189 | }
190 | else
191 | {
192 | qDebug() << __func__ << query.lastError().text();
193 | closeDatabase();
194 | }
195 |
196 | return friends;
197 | }
198 |
199 | QMap > Database::getUserFriendsInfo(const QString &username)
200 | {
201 | QString query_friends = "SELECT user_group AS user_group, user_friend AS user_friend, user_unread AS unreadMessage "
202 | "FROM users "
203 | "WHERE user_username = '" + username + "' "
204 | "UNION ALL "
205 | "SELECT friend_group AS user_group, user_username AS user_friend, friend_unread AS unreadMessage "
206 | "FROM users "
207 | "WHERE user_friend = '" + username + "';";
208 | QSqlQuery query(m_database);
209 | QMap > friendsInfo;
210 | if (query.exec(query_friends))
211 | {
212 | while (query.next())
213 | {
214 | if (query.isValid())
215 | {
216 | QString user_group = query.value(0).toString();
217 | QString user_friend = query.value(1).toString();
218 | int user_unread = query.value(2).toInt();
219 | FriendInfo info = { user_friend, user_unread };
220 | friendsInfo[user_group].append(info);
221 | }
222 | }
223 | }
224 | else
225 | {
226 | qDebug() << __func__ << query.lastError().text();
227 | closeDatabase();
228 | }
229 |
230 | return friendsInfo;
231 | }
232 |
233 | bool Database::addUnreadMessage(const QString &sender, const QString &receiver)
234 | {
235 | QString query_unread = "SELECT user_unread AS unreadMessage "
236 | "FROM users "
237 | "WHERE user_username = '" + receiver + "' "
238 | "UNION ALL "
239 | "SELECT friend_unread AS unreadMessage "
240 | "FROM users "
241 | "WHERE user_friend = '" + receiver + "';";
242 |
243 | QSqlQuery query(m_database);
244 |
245 | if (query.exec(query_unread))
246 | {
247 | query.next();
248 | int unreadMessage = query.value(0).toInt();
249 | QString query_update1 = "UPDATE users "
250 | "SET user_unread = " + QString::number(unreadMessage + 1) +
251 | " WHERE user_username = '" + receiver + "' AND user_friend = '" + sender + "';";
252 |
253 | QString query_update2 = "UPDATE users "
254 | "SET friend_unread = " + QString::number(unreadMessage + 1) +
255 | " WHERE user_username = '" + sender + "' AND user_friend = '" + receiver + "';";
256 | if (query.exec(query_update1) && query.exec(query_update2))
257 | {
258 | return true;
259 | }
260 | else
261 | {
262 | qDebug() << __func__ << query.lastError().text();
263 | closeDatabase();
264 | return false;
265 | }
266 | }
267 | else
268 | {
269 | qDebug() << __func__ << query.lastError().text();
270 | closeDatabase();
271 | return false;
272 | }
273 | }
274 |
275 | bool Database::tableExists()
276 | {
277 | if (!openDatabase())
278 | return false;
279 |
280 | QString query_create_users = "CREATE TABLE IF NOT EXISTS users"
281 | "("
282 | " user_username varchar(10) NOT NULL,"
283 | " user_friend varchar(10) NOT NULL,"
284 | " user_group varchar(16) DEFAULT '我的好友',"
285 | " friend_group varchar(16) DEFAULT '我的好友',"
286 | " user_unread int DEFAULT 0,"
287 | " friend_unread int DEFAULT 0"
288 | ");";
289 | QString query_create_info = "CREATE TABLE IF NOT EXISTS info"
290 | "("
291 | " user_username varchar(10) NOT NULL PRIMARY KEY,"
292 | " user_password varchar(32) NOT NULL,"
293 | " user_nickname varchar(32) DEFAULT 'USER',"
294 | " user_headImage varchar(32) DEFAULT 'qrc:/image/winIcon.png',"
295 | " user_background varchar(32) DEFAULT 'qrc:/image/Background/7.jpg',"
296 | " user_gender varchar(2) DEFAULT '男',"
297 | " user_birthday text DEFAULT '2019-01-01',"
298 | " user_signature varchar(64) DEFAULT NULL,"
299 | " user_level int DEFAULT 1"
300 | ");";
301 | QSqlQuery query(m_database);
302 | if (query.exec(query_create_users))
303 | {
304 | if (query.exec(query_create_info))
305 | {
306 | return true;
307 | }
308 | else
309 | {
310 | qDebug() << __func__ << query.lastError().text();
311 | closeDatabase();
312 | return false;
313 | }
314 | }
315 | else
316 | {
317 | qDebug() << __func__ << query.lastError().text();
318 | closeDatabase();
319 | return false;
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/src/database.h:
--------------------------------------------------------------------------------
1 | #ifndef DATABASE_H
2 | #define DATABASE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | struct UserInfo
10 | {
11 | QString username;
12 | QString password;
13 | QString nickname;
14 | QString headImage;
15 | QString background;
16 | QString gender;
17 | QString birthday;
18 | QString signature;
19 | int level;
20 |
21 | UserInfo()
22 | : username(""), password(""), nickname("") , headImage("")
23 | , background(""), gender(""), birthday(""), signature(""), level(0)
24 | {
25 |
26 | }
27 |
28 | bool isEmpty() const { return username.isEmpty(); }
29 | };
30 |
31 | struct FriendInfo
32 | {
33 | QString friendname;
34 | int unreadMessage;
35 | };
36 |
37 | QDebug operator<<(QDebug debug, const UserInfo &info);
38 |
39 | class Database : public QObject
40 | {
41 | Q_OBJECT
42 |
43 | public:
44 | Database(const QString &connectionName, QObject *parent = nullptr);
45 | ~Database();
46 |
47 | QString name() const { return m_database.connectionName(); }
48 | bool openDatabase();
49 | void closeDatabase();
50 |
51 | bool createUser(const UserInfo &info);
52 | bool addFriend(const QString &username, const QString &friendname);
53 |
54 | void setUserInfo(const UserInfo &info);
55 | UserInfo getUserInfo(const QString &username);
56 |
57 | QStringList getUserFriends(const QString &username);
58 | QMap > getUserFriendsInfo(const QString &username);
59 |
60 | //增加一条未读记录
61 | bool addUnreadMessage(const QString &sender, const QString &receiver);
62 |
63 | private:
64 | bool tableExists();
65 |
66 | private:
67 | QMutex m_mutex;
68 | QString m_connectionName;
69 | QSqlDatabase m_database;
70 | };
71 |
72 | #endif // DATABASE_H
73 |
--------------------------------------------------------------------------------
/src/framelesswindow.cpp:
--------------------------------------------------------------------------------
1 | #include "framelesswindow.h"
2 | #include
3 |
4 | #ifdef Q_OS_WIN
5 | #include
6 | #endif
7 |
8 | FramelessWindow::FramelessWindow(QQuickWindow *parent)
9 | : QQuickWindow(parent)
10 | {
11 | m_minimumWidth = 0;
12 | m_minimumHeight = 0;
13 | m_maximumWidth = 9999;
14 | m_maximumHeight = 9999;
15 | m_mousePenetrate = false;
16 | m_topHint = false;
17 | m_taskbarHint = false;
18 |
19 | setFlags(flags() | Qt::Tool | Qt::FramelessWindowHint);
20 | setColor(Qt::transparent);
21 |
22 | /*#if defined(Q_OS_WIN)
23 | HWND hwnd = reinterpret_cast(winId());
24 | DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);
25 | class_style &= ~CS_DROPSHADOW;
26 | ::SetClassLong(hwnd, GCL_STYLE, class_style); // windows系统函数
27 | #endif*/
28 | }
29 |
30 | FramelessWindow::~FramelessWindow()
31 | {
32 |
33 | }
34 |
35 | QString FramelessWindow::windowIcon() const
36 | {
37 | return m_windowIcon;
38 | }
39 |
40 | QPoint FramelessWindow::coord() const
41 | {
42 | return QPoint(x(), y());
43 | }
44 |
45 | int FramelessWindow::width() const
46 | {
47 | return m_width;
48 | }
49 |
50 | int FramelessWindow::height() const
51 | {
52 | return m_height;
53 | }
54 |
55 | int FramelessWindow::actualWidth() const
56 | {
57 | return QQuickWindow::width();
58 | }
59 |
60 | int FramelessWindow::actualHeight() const
61 | {
62 | return QQuickWindow::height();
63 | }
64 |
65 | int FramelessWindow::minimumWidth() const
66 | {
67 | return m_minimumWidth;
68 | }
69 |
70 | int FramelessWindow::minimumHeight() const
71 | {
72 | return m_minimumHeight;
73 | }
74 |
75 | int FramelessWindow::maximumWidth() const
76 | {
77 | return m_maximumWidth;
78 | }
79 |
80 | int FramelessWindow::maximumHeight() const
81 | {
82 | return m_maximumHeight;
83 | }
84 |
85 | bool FramelessWindow::mousePenetrate() const
86 | {
87 | return m_mousePenetrate;
88 | }
89 |
90 | bool FramelessWindow::topHint() const
91 | {
92 | return m_topHint;
93 | }
94 |
95 | bool FramelessWindow::taskbarHint() const
96 | {
97 | return m_taskbarHint;
98 | }
99 |
100 | void FramelessWindow::setWindowIcon(const QString &arg)
101 | {
102 | if (m_windowIcon != arg)
103 | {
104 | setIcon(QIcon(arg));
105 | m_windowIcon = arg;
106 | emit windowIconChanged(arg);
107 | }
108 | }
109 |
110 | void FramelessWindow::setCoord(const QPoint &arg)
111 | {
112 | if (coord() != arg)
113 | {
114 | setX(arg.x());
115 | setY(arg.y());
116 | emit coordChanged(arg);
117 | }
118 | }
119 |
120 | void FramelessWindow::setWidth(int arg)
121 | {
122 | if (arg <= m_maximumWidth && arg >= m_minimumWidth)
123 | {
124 | m_width = arg;
125 | contentItem()->setWidth(arg);
126 | emit widthChanged(arg);
127 | }
128 | }
129 |
130 | void FramelessWindow::setHeight(int arg)
131 | {
132 | if (arg <= m_maximumHeight && arg >= m_minimumHeight)
133 | {
134 | m_height = arg;
135 | contentItem()->setHeight(arg);
136 | emit heightChanged(arg);
137 | }
138 | }
139 |
140 | void FramelessWindow::setActualWidth(int arg)
141 | {
142 | if (actualWidth() != arg)
143 | {
144 | QQuickWindow::setWidth(arg);
145 | emit actualWidthChanged(arg);
146 | }
147 | }
148 |
149 | void FramelessWindow::setActualHeight(int arg)
150 | {
151 | if (actualHeight() != arg)
152 | {
153 | QQuickWindow::setHeight(arg);
154 | emit actualHeightChanged(arg);
155 | }
156 | }
157 |
158 | void FramelessWindow::setMinimumWidth(int arg)
159 | {
160 | if (m_minimumWidth != arg)
161 | {
162 | m_minimumWidth = arg;
163 | emit minimumWidthChanged(arg);
164 | }
165 | }
166 |
167 | void FramelessWindow::setMinimumHeight(int arg)
168 | {
169 | if (m_minimumHeight != arg)
170 | {
171 | m_minimumHeight = arg;
172 | emit minimumHeightChanged(arg);
173 | }
174 | }
175 |
176 | void FramelessWindow::setMaximumWidth(int arg)
177 | {
178 | if (m_maximumWidth != arg)
179 | {
180 | m_maximumWidth = arg;
181 | emit maximumWidthChanged(arg);
182 | }
183 | }
184 |
185 | void FramelessWindow::setMaximumHeight(int arg)
186 | {
187 | if (m_maximumHeight != arg)
188 | {
189 | m_maximumHeight = arg;
190 | emit maximumHeightChanged(arg);
191 | }
192 | }
193 |
194 | void FramelessWindow::setMousePenetrate(bool arg)
195 | {
196 | if (m_mousePenetrate != arg)
197 | {
198 | #if defined(Q_OS_WIN)
199 | HWND my_hwnd = (HWND)this->winId ();
200 | if(arg)
201 | {
202 | SetWindowLong(my_hwnd, GWL_EXSTYLE,
203 | GetWindowLong(my_hwnd, GWL_EXSTYLE) | WS_EX_TRANSPARENT);
204 | }
205 | else
206 | {
207 | SetWindowLong(my_hwnd, GWL_EXSTYLE,
208 | GetWindowLong(my_hwnd, GWL_EXSTYLE)&(~WS_EX_TRANSPARENT));
209 | }
210 | #endif
211 | m_mousePenetrate = arg;
212 | emit mousePenetrateChanged();
213 | }
214 | }
215 |
216 | void FramelessWindow::setTopHint(bool arg)
217 | {
218 | if (m_topHint != arg)
219 | {
220 | if (arg)
221 | setFlags(flags() | Qt::WindowStaysOnTopHint);
222 | else
223 | setFlags(flags() & ~Qt::WindowStaysOnTopHint);
224 | m_topHint = arg;
225 | emit topHintChanged();
226 | }
227 | }
228 |
229 | void FramelessWindow::setTaskbarHint(bool arg)
230 | {
231 | if (m_taskbarHint != arg)
232 | {
233 | if(arg)
234 | setFlags(flags() & (~Qt::Tool) | Qt::Window);
235 | else
236 | setFlags(flags() | Qt::Tool);
237 | m_taskbarHint = arg;
238 | emit taskbarHintChanged();
239 | }
240 | }
241 |
242 | void FramelessWindow::close()
243 | {
244 | emit closed();
245 | deleteLater();
246 | }
247 |
248 | bool FramelessWindow::event(QEvent *ev)
249 | {
250 | if (ev->type() == QEvent::Enter) //鼠标进入
251 | {
252 | emit entered();
253 | }
254 | else if (ev->type() == QEvent::Leave) //鼠标离开
255 | {
256 | emit exited();
257 | }
258 |
259 | return QQuickWindow::event(ev);
260 | }
261 |
--------------------------------------------------------------------------------
/src/framelesswindow.h:
--------------------------------------------------------------------------------
1 | #ifndef FRAMELESSWINDOW_H
2 | #define FRAMELESSWINDOW_H
3 |
4 | #include
5 |
6 | class FramelessWindow : public QQuickWindow
7 | {
8 | Q_OBJECT
9 |
10 | Q_PROPERTY(QString windowIcon READ windowIcon WRITE setWindowIcon NOTIFY windowIconChanged)
11 | Q_PROPERTY(QPoint coord READ coord WRITE setCoord NOTIFY coordChanged)
12 | Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged)
13 | Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged)
14 | Q_PROPERTY(int actualWidth READ actualWidth WRITE setActualWidth NOTIFY actualWidthChanged)
15 | Q_PROPERTY(int actualHeight READ actualHeight WRITE setActualHeight NOTIFY actualHeightChanged)
16 | Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged)
17 | Q_PROPERTY(int minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged)
18 | Q_PROPERTY(int maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged)
19 | Q_PROPERTY(int maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged)
20 | //是否穿透鼠标
21 | Q_PROPERTY(bool mousePenetrate READ mousePenetrate WRITE setMousePenetrate NOTIFY mousePenetrateChanged)
22 | //是否显示在最前
23 | Q_PROPERTY(bool topHint READ topHint WRITE setTopHint NOTIFY topHintChanged)
24 | Q_PROPERTY(bool taskbarHint READ taskbarHint WRITE setTaskbarHint NOTIFY taskbarHintChanged)
25 |
26 | public:
27 | FramelessWindow(QQuickWindow *parent = nullptr);
28 | ~FramelessWindow();
29 |
30 | QString windowIcon() const;
31 | void setWindowIcon(const QString &arg);
32 |
33 | QPoint coord() const;
34 | void setCoord(const QPoint &arg);
35 |
36 | int width() const;
37 | void setWidth(int arg);
38 |
39 | int height() const;
40 | void setHeight(int arg);
41 |
42 | int actualWidth() const;
43 | void setActualWidth(int arg);
44 |
45 | int actualHeight() const;
46 | void setActualHeight(int arg);
47 |
48 | int minimumWidth() const;
49 | void setMinimumWidth(int arg);
50 |
51 | int minimumHeight() const;
52 | void setMinimumHeight(int arg);
53 |
54 | int maximumWidth() const;
55 | void setMaximumWidth(int arg);
56 |
57 | int maximumHeight() const;
58 | void setMaximumHeight(int arg);
59 |
60 | bool mousePenetrate() const;
61 | void setMousePenetrate(bool arg);
62 |
63 | bool topHint() const;
64 | void setTopHint(bool arg);
65 |
66 | bool taskbarHint() const;
67 | void setTaskbarHint(bool arg);
68 |
69 | signals:
70 | void windowIconChanged(const QString &arg);
71 | void coordChanged(const QPoint &arg);
72 | void widthChanged(int arg);
73 | void heightChanged(int arg);
74 | void actualWidthChanged(int arg);
75 | void actualHeightChanged(int arg);
76 | void minimumWidthChanged(int arg);
77 | void minimumHeightChanged(int arg);
78 | void maximumWidthChanged(int arg);
79 | void maximumHeightChanged(int arg);
80 | void mousePenetrateChanged();
81 | void topHintChanged();
82 | void taskbarHintChanged();
83 |
84 | void entered();
85 | void exited();
86 | void closed();
87 |
88 | public slots:
89 | void close();
90 |
91 | protected:
92 | bool event(QEvent *ev);
93 |
94 | private:
95 | QString m_windowIcon;
96 | int m_width;
97 | int m_height;
98 | int m_minimumWidth;
99 | int m_minimumHeight;
100 | int m_maximumWidth;
101 | int m_maximumHeight;
102 | bool m_mousePenetrate;
103 | bool m_topHint;
104 | bool m_taskbarHint;
105 | };
106 |
107 | #endif // FRAMELESSWINDOW_H
108 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "chattcpserver.h"
2 | #include "framelesswindow.h"
3 |
4 | #include
5 | #include
6 |
7 | int main(int argc, char *argv[])
8 | {
9 | QGuiApplication app(argc, argv);
10 |
11 | qmlRegisterType("an.framelessWindow", 1, 0, "FramelessWindow");
12 | qRegisterMetaType("msg_t");
13 | qRegisterMetaType("msg_option_t");
14 |
15 | QQmlApplicationEngine engine;
16 | ChatTcpServer server(&engine);
17 | if (!server.listen(QHostAddress::AnyIPv4, server_port))
18 | {
19 | QGuiApplication::exit(1);
20 | }
21 | else server.loadWindow();
22 |
23 | return app.exec();
24 | }
25 |
--------------------------------------------------------------------------------
/src/protocol.cpp:
--------------------------------------------------------------------------------
1 | #include "protocol.h"
2 | #include
3 |
4 | extern QDebug operator<<(QDebug debug, const MessageHeader &header)
5 | {
6 | debug << hex << header.flag
7 | << header.type
8 | << header.size
9 | << header.option
10 | << header.sender
11 | << header.receiver
12 | << header.md5;
13 |
14 | return debug;
15 | }
16 |
17 | extern QDebug operator<<(QDebug debug, const Message &message)
18 | {
19 | debug << message.header << message.data;
20 |
21 | return debug;
22 | }
23 |
24 | extern QDataStream& operator<<(QDataStream &out, const MessageHeader &header)
25 | {
26 | out << header.flag
27 | << header.type
28 | << header.size
29 | << header.option
30 | << header.sender
31 | << header.receiver
32 | << header.md5;
33 |
34 | return out;
35 | }
36 |
37 | extern QDataStream& operator>>(QDataStream &in, MessageHeader &header)
38 | {
39 | in >> header.flag
40 | >> header.type
41 | >> header.size
42 | >> header.option
43 | >> header.sender
44 | >> header.receiver
45 | >> header.md5;
46 |
47 | return in;
48 | }
49 |
50 | extern QDataStream& operator<<(QDataStream &out, const Message &message)
51 | {
52 | out << message.header
53 | << message.data;
54 |
55 | return out;
56 | }
57 |
58 | extern QDataStream& operator>>(QDataStream &in, Message &message)
59 | {
60 | in >> message.header
61 | >> message.data;
62 |
63 | return in;
64 | }
65 |
--------------------------------------------------------------------------------
/src/protocol.h:
--------------------------------------------------------------------------------
1 | #ifndef PROTOCOL_H
2 | #define PROTOCOL_H
3 |
4 | #include
5 | #include
6 |
7 | typedef quint32 msg_flag_t;
8 | typedef quint8 msg_t;
9 | typedef quint32 msg_size_t;
10 | typedef quint8 msg_option_t;
11 |
12 | //用于测试
13 | #define server_ip "10.220.3.25"
14 | #define server_port 43800
15 |
16 | //消息头的标志
17 | #define MSG_FLAG 0xF8AD951A
18 |
19 | //消息类型
20 | //系统类型 0x10 ~ 0x40
21 | #define MSG_IS_SYSTEM(x) (x <= 0x40)
22 | #define MT_CHECK 0x10 //验证
23 | #define MT_HEARTBEAT 0x11 //心跳
24 | #define MT_USERINFO 0x12 //用户信息
25 | #define MT_STATECHANGE 0x13 //状态变化
26 | #define MT_SEARCH 0x14 //获取信息
27 | #define MT_ADDFRIEND 0x15 //添加好友
28 | #define MT_REGISTER 0x16 //注册账户
29 | #define MT_UNKNOW 0x30 //未知
30 |
31 | #define CHECK_SUCCESS "SUCCESS" //验证成功
32 | #define CHECK_FAILURE "FAILURE" //验证失败
33 |
34 | #define CLIENT_ID "CLIENT" //客户端默认ID
35 | #define SERVER_ID "SERVER" //服务器ID
36 | #define HEARTBEAT "HEARTBEAT" //心跳数据
37 | #define USERINFO "USERINFO" //用户信息
38 |
39 | #define ADDFRIEND "ADDFRIEND" //添加好友
40 | //添加成功将发送其信息
41 | #define ADD_SUCCESS "SUCCESS" //添加成功
42 | #define ADD_FAILURE "FAILURE" //添加失败
43 |
44 | //注册成功/失败消息
45 | #define REG_SUCCESS "SUCCESS" //注册成功
46 | #define REG_FAILURE "FAILURE" //注册失败
47 |
48 | //用户类型 0x41 ~ 0xFF
49 | #define MSG_IS_USER(x) (!(MSG_IS_SYSTEM(x)))
50 | #define MT_SHAKE 0x41 //窗口抖动
51 | #define MT_TEXT 0x42 //普通文本
52 | #define MT_IMAGE 0x43 //图像
53 | #define MT_FILE 0x44 //文件
54 |
55 | //选项类型
56 | #define MO_NULL 0x10 //无类型
57 | #define MO_UPLOAD 0x11 //上传
58 | #define MO_DOWNLOAD 0x12 //下载
59 |
60 | /*
61 | * \ 消息标志flag \\ 消息类型type \\ 消息大小size \\ 选项类型option \\ 发送者ID \\ 接收者ID \\ MD5验证 \
62 | * { 消息头 }
63 | * \ 数据data \ ... \ 数据data \
64 | * { 数据 }
65 | */
66 |
67 | struct MessageHeader
68 | {
69 | MessageHeader()
70 | : flag(0), type(0), size(0), option(0),
71 | sender(QByteArray()), receiver(QByteArray()), md5(QByteArray()) {}
72 | MessageHeader(msg_flag_t f, msg_t t, msg_size_t s, msg_option_t o,
73 | const QByteArray &sr, const QByteArray &rr, const QByteArray &m)
74 | : flag(f), type(t), size(s), option(o), sender(sr), receiver(rr), md5(m) {}
75 |
76 | bool isEmpty() const
77 | {
78 | return flag == 0 ||
79 | type == 0 ||
80 | size == 0 ||
81 | option == 0 ||
82 | sender.isEmpty() ||
83 | receiver.isEmpty() ||
84 | md5.isEmpty();
85 | }
86 |
87 | int getSize() const
88 | {
89 | return int(sizeof(flag)) +
90 | int(sizeof(type)) +
91 | int(sizeof(size)) +
92 | int(sizeof(option)) +
93 | sender.size() +
94 | receiver.size() +
95 | md5.size() +
96 | 3 * 4; //有三个QByteArray,每个会在前面加4字节大小
97 | }
98 |
99 | msg_flag_t flag;
100 | msg_t type;
101 | msg_size_t size;
102 | msg_option_t option;
103 | QByteArray sender; //Latin-1
104 | QByteArray receiver; //Latin-1
105 | QByteArray md5; //md5(base64 data)
106 | };
107 |
108 | struct Message
109 | {
110 | Message() : header(MessageHeader()), data(QByteArray()) {}
111 | Message(const MessageHeader &h, const QByteArray &d) : header(h), data(d) {}
112 | bool isEmpty() const { return header.isEmpty() || data.isEmpty(); }
113 |
114 | MessageHeader header;
115 | QByteArray data; //Local8Bit
116 | };
117 |
118 | msg_flag_t inline get_flag(MessageHeader &header) { return header.flag; }
119 | msg_flag_t inline get_flag(Message &message) { return get_flag(message.header); }
120 | msg_t inline get_type(MessageHeader &header) { return header.type; }
121 | msg_t inline get_type(Message &message) { return get_type(message.header); }
122 | msg_size_t inline get_size(MessageHeader &header) { return header.size; }
123 | msg_size_t inline get_size(Message &message) { return get_size(message.header); }
124 | msg_option_t inline get_option(MessageHeader &header) { return header.option; }
125 | msg_option_t inline get_option(Message &message) { return get_option(message.header); }
126 | QByteArray inline get_sender(MessageHeader &header) { return header.sender; }
127 | QByteArray inline get_sender(Message &message) { return get_sender(message.header); }
128 | QByteArray inline get_receiver(MessageHeader &header) { return header.receiver; }
129 | QByteArray inline get_receiver(Message &message) { return get_receiver(message.header); }
130 | QByteArray inline get_md5(MessageHeader &header) { return header.md5; }
131 | QByteArray inline get_md5(Message &message) { return get_md5(message.header); }
132 |
133 | QDebug operator<<(QDebug debug, const MessageHeader &header);
134 | QDebug operator<<(QDebug debug, const Message &message);
135 | QDataStream& operator<<(QDataStream &out, const MessageHeader &header);
136 | QDataStream& operator>>(QDataStream &in, MessageHeader &header);
137 | QDataStream& operator<<(QDataStream &out, const Message &message);
138 | QDataStream& operator>>(QDataStream &in, Message &message);
139 |
140 | #endif // PROTOCOL_H
141 |
--------------------------------------------------------------------------------
/test.sql:
--------------------------------------------------------------------------------
1 | SELECT user_group AS user_group, user_friend AS user_friend, user_unread AS unreadMessage
2 | FROM users.users
3 | WHERE user_username = "843261040"
4 | UNION ALL
5 | SELECT friend_group AS user_group, user_username AS user_friend, friend_unread AS unreadMessage
6 | FROM users.users
7 | WHERE user_friend = "843261040";
8 |
9 | SELECT user_unread AS unreadMessage
10 | FROM users.users
11 | WHERE user_username ="843261040"
12 | UNION ALL
13 | SELECT friend_unread AS unreadMessage
14 | FROM users.users
15 | WHERE user_friend ="843261040";
16 |
17 |
18 | /*UPDATE users.users
19 | SET user_unread = 3
20 | WHERE user_username = "00000001" AND user_friend = "843261040";
21 | UPDATE users.users
22 | SET friend_unread = 3
23 | WHERE user_username = "843261040" AND user_friend = "00000001";*/
24 |
25 | /*UPDATE users.info
26 | SET user_password = '00000000000', user_gender = '男'
27 | WHERE user_username = '843261040';*/
28 |
29 |
30 | INSERT INTO users.info
31 | VALUES("843261040", "00000000000", "MPS", "qrc:/image/head1.jpg", "qrc:/image/Background/7.jpg", "男", "1996-07-27", "嗷嗷嗷嗷嗷嗷嗷", 999);
32 | INSERT INTO users.info
33 | VALUES("00000001", "00000000000", "mps1", "qrc:/image/HeadImage/head1.jpg", "qrc:/image/Background/7.jpg","男", "1995-01-23", "我不爱你了、", 37);
34 | INSERT INTO users.info
35 | VALUES("00000002", "00000000000", "mps2", "qrc:/image/HeadImage/head2.jpg", "qrc:/image/Background/7.jpg","男", "1998-10-12", "哎 o(︶︿︶)o ", 16);
36 | INSERT INTO users.info
37 | VALUES("00000003", "00000000000", "mps3", "qrc:/image/HeadImage/head3.jpg", "qrc:/image/Background/7.jpg","女", "1997-08-08", "累了、", 23);
--------------------------------------------------------------------------------
/users/843261040/headImage/head1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/users/843261040/headImage/head1.jpg
--------------------------------------------------------------------------------
/users/users.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mengps/MChatServer/5409a4577492260210535b60770e4fdf37243c40/users/users.db
--------------------------------------------------------------------------------