-
Notifications
You must be signed in to change notification settings - Fork 1
/
UChat-基于python的安全即时通讯系统.html
2189 lines (2003 loc) · 91.7 KB
/
UChat-基于python的安全即时通讯系统.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.2">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"leeyuxun.github.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":true,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":true,"mediumzoom":false,"lazyload":false,"pangu":true,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"./public/search.xml"};
</script>
<meta name="description" content="目的设计完成简易的安全即时通讯系统,实现类似于QQ的聊天软件;">
<meta property="og:type" content="article">
<meta property="og:title" content="UChat-基于python的安全即时通讯系统">
<meta property="og:url" content="https://leeyuxun.github.io/UChat-%E5%9F%BA%E4%BA%8Epython%E7%9A%84%E5%AE%89%E5%85%A8%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF%E7%B3%BB%E7%BB%9F.html">
<meta property="og:site_name" content="Leeyuxun の note">
<meta property="og:description" content="目的设计完成简易的安全即时通讯系统,实现类似于QQ的聊天软件;">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714124535.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714124653.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714133500.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134257.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134437.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134505.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134543.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134625.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134751.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134808.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134732.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714140712.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145452.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145528.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145654.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145755.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145832.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714145916.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150022.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150145.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150237.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150347.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150443.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150547.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150653.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150733.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150811.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714150901.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714151012.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714151144.png">
<meta property="og:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714151209.png">
<meta property="article:published_time" content="2020-07-14T07:52:20.000Z">
<meta property="article:modified_time" content="2023-05-07T07:37:53.571Z">
<meta property="article:author" content="李钰璕">
<meta property="article:tag" content="安全即时通讯软件">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714124535.png">
<link rel="canonical" href="https://leeyuxun.github.io/UChat-%E5%9F%BA%E4%BA%8Epython%E7%9A%84%E5%AE%89%E5%85%A8%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF%E7%B3%BB%E7%BB%9F.html">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : false,
isPost : true,
lang : 'zh-CN'
};
</script>
<title>UChat-基于python的安全即时通讯系统 | Leeyuxun の note</title>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V3499K2XZY"></script>
<script>
if (CONFIG.hostname === location.hostname) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-V3499K2XZY');
}
</script>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?4d72a66931dff6410b32974da2e3df61";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
<style>mjx-container[jax="SVG"] {
direction: ltr;
}
mjx-container[jax="SVG"] > svg {
overflow: visible;
}
mjx-container[jax="SVG"][display="true"] {
display: block;
text-align: center;
margin: 1em 0;
}
mjx-container[jax="SVG"][justify="left"] {
text-align: left;
}
mjx-container[jax="SVG"][justify="right"] {
text-align: right;
}
g[data-mml-node="merror"] > g {
fill: red;
stroke: red;
}
g[data-mml-node="merror"] > rect[data-background] {
fill: yellow;
stroke: none;
}
g[data-mml-node="mtable"] > line[data-line] {
stroke-width: 70px;
fill: none;
}
g[data-mml-node="mtable"] > rect[data-frame] {
stroke-width: 70px;
fill: none;
}
g[data-mml-node="mtable"] > .mjx-dashed {
stroke-dasharray: 140;
}
g[data-mml-node="mtable"] > .mjx-dotted {
stroke-linecap: round;
stroke-dasharray: 0,140;
}
g[data-mml-node="mtable"] > svg {
overflow: visible;
}
[jax="SVG"] mjx-tool {
display: inline-block;
position: relative;
width: 0;
height: 0;
}
[jax="SVG"] mjx-tool > mjx-tip {
position: absolute;
top: 0;
left: 0;
}
mjx-tool > mjx-tip {
display: inline-block;
padding: .2em;
border: 1px solid #888;
font-size: 70%;
background-color: #F8F8F8;
color: black;
box-shadow: 2px 2px 5px #AAAAAA;
}
g[data-mml-node="maction"][data-toggle] {
cursor: pointer;
}
mjx-status {
display: block;
position: fixed;
left: 1em;
bottom: 1em;
min-width: 25%;
padding: .2em .4em;
border: 1px solid #888;
font-size: 90%;
background-color: #F8F8F8;
color: black;
}
foreignObject[data-mjx-xml] {
font-family: initial;
line-height: normal;
overflow: visible;
}
.MathJax path {
stroke-width: 3;
}
mjx-container[display="true"] {
overflow: auto hidden;
}
mjx-container[display="true"] + br {
display: none;
}
</style></head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">Leeyuxun の note</h1>
<span class="logo-line-after"><i></i></span>
</a>
<p class="site-subtitle" itemprop="description">BUPT | SCSS</p>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
<i class="fa fa-search fa-fw fa-lg"></i>
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>
</li>
<li class="menu-item menu-item-links">
<a href="/links/" rel="section"><i class="fa fa-link fa-fw"></i>友链</a>
</li>
<li class="menu-item menu-item-search">
<a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
</a>
</li>
</ul>
</nav>
<div class="search-pop-overlay">
<div class="popup search-popup">
<div class="search-header">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<div class="search-input-container">
<input autocomplete="off" autocapitalize="off"
placeholder="搜索..." spellcheck="false"
type="search" class="search-input">
</div>
<span class="popup-btn-close">
<i class="fa fa-times-circle"></i>
</span>
</div>
<div id="search-result">
<div id="no-result">
<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
</div>
</div>
</div>
</div>
</div>
</header>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content post posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="https://leeyuxun.github.io/UChat-%E5%9F%BA%E4%BA%8Epython%E7%9A%84%E5%AE%89%E5%85%A8%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF%E7%B3%BB%E7%BB%9F.html">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.png">
<meta itemprop="name" content="李钰璕">
<meta itemprop="description" content="安全学习笔记">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Leeyuxun の note">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
UChat-基于python的安全即时通讯系统
</h1>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2020-07-14 15:52:20" itemprop="dateCreated datePublished" datetime="2020-07-14T15:52:20+08:00">2020-07-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2023-05-07 15:37:53" itemprop="dateModified" datetime="2023-05-07T15:37:53+08:00">2023-05-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/personal-tools/" itemprop="url" rel="index"><span itemprop="name">personal tools</span></a>
</span>
</span>
<span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv" style="display: none;">
<span class="post-meta-item-icon">
<i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读次数:</span>
<span id="busuanzi_value_page_pv"></span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h2><p>设计完成简易的安全即时通讯系统,实现类似于QQ的聊天软件;<span id="more"></span></p>
<h2 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h2><h3 id="功能需求"><a href="#功能需求" class="headerlink" title="功能需求"></a>功能需求</h3><ol>
<li><p>聊天客户端</p>
<ol>
<li>注册:用户与集中服务器通信完成注册,包括用户名、密码、邮箱、性别、年龄、数字证书等信息传输,其中数字证书包含公钥、用户名、邮箱等信息。私钥单独保存在客户端一个文件夹下不进行传输;能显示用户名、邮箱不符合格式规范或者重复,空输入等错误信息。</li>
<li>认证登录:客户端与集中服务器通信完成用户名、口令认证登录;能显示用户名、密码错误导致的登录错误信息。还有已登录账号再次登录时的多重登录检验,并将之前登陆的账号顶下去。</li>
<li>好友管理:用户可通过服务器进行搜索、添加、删除好友。</li>
<li>即时通信:用户通过客户端实现与好友的聊天,包括文字、图片传输。文字可实现字体颜色和大小的改变。</li>
<li>聊天记录:客户端能够保存聊天记录并且可以查看聊天记录。</li>
<li>消息加解密:采用D-H体制协商加密秘钥,用对称密码AES算法进行加解密。</li>
<li>消息摘要:使用MD5算法实现消息摘要认证功能,确保发送消息的完整性。</li>
<li>用户未读消息提醒:红点标注未读消息数目,并按最后发送消息时间排列好友列表。</li>
<li>用户离线后消息处理:用户上线后及时接收到离线时好友发送的消息。</li>
</ol>
<p>功能结构图</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714124535.png"></p>
</li>
<li><p>集中服务器</p>
<ol>
<li>用户注册:与用户通信完成注册,对用户名和邮箱格式、是否重复,输入不规范等做必要的检验,接收客户端的数字证书,发送服务端数字证书。</li>
<li>登录验证:用户登录时,验证用户名和密码是否正确,并向客户端返回登录结果。如信息正确,就将在线好友用户发给该用户,将该用户的状态发给各在线好友用户,同时在服务器端显示出来。</li>
<li>用户公钥,证书提供:用户向好友发送消息时,与服务器建立安全连接获取好友的证书信息,服务器控制client.socket像好友用户发送信息,实现用户之间的通信。</li>
<li>用户在线状态维护:当用户在线时,记录保存用户的在线状态、IP地址、端口号。</li>
<li>用户消息列表实时发放:由监听函数将操作实时加入到执行函数列表中递归执行。向用户发送其好友列表的在线离线情况,包括好友用户名、IP地址、端口号。并按照最后发消息的时间对好友消息列表进行排序。</li>
</ol>
<p>功能结构图</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714124653.png"></p>
</li>
<li><p>高级功能</p>
<ol>
<li>离线用户消息通知:暂时存储离线用户的消息,用户上线后,显示未读的消息并用红点标注;</li>
<li>好友在线离线功能实时更新;</li>
<li>限制账号只能一处登录:一个账号只能在一处登录,在别处登录时会把原先的登录踢下线;</li>
<li>支持群聊功能:可以创建群聊,并根据群号加入群聊;</li>
<li>聊天时字体大小颜色可更改;</li>
<li>支持聊天各类图像文件的缓存。</li>
</ol>
</li>
</ol>
<h3 id="数据需求"><a href="#数据需求" class="headerlink" title="数据需求"></a>数据需求</h3><ol>
<li><p>客户端</p>
<p>客户端登录后加过的好友和加入的群聊需要从数据库中调出信息并在前端反馈呈现出来。客户端的聊天记录可以存储在数据库中,用到时直接读取返回消息历史。</p>
</li>
<li><p>集中服务器</p>
<ol>
<li><p>users表:用户信息表,存储用户基本信息,包括用户ID(id)、用户名(username)、密码(password)、电子邮箱(email)、用户登录IP地址(ip)、用户登录端口(port)、性别(sex)、年龄(age)、公钥(pk)。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>数据类型</th>
<th>主键</th>
<th>是否唯一</th>
<th>是否为空</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>用户id</td>
</tr>
<tr>
<td>username</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>用户名</td>
</tr>
<tr>
<td>password</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>密码</td>
</tr>
<tr>
<td>email</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>邮箱</td>
</tr>
<tr>
<td>ip</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>登录IP</td>
</tr>
<tr>
<td>port</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>登录端口</td>
</tr>
<tr>
<td>sex</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>性别</td>
</tr>
<tr>
<td>age</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>姓名</td>
</tr>
<tr>
<td>pk</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>公钥</td>
</tr>
</tbody></table>
</li>
<li><p>friends表:存储用户的好友信息,包括用户id(from_user_id)、好友id(to_user_id)、加好友请求是否接受(accepted)。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>数据类型</th>
<th>主键</th>
<th>是否唯一</th>
<th>是否为空</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>from_user_id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>本人ID</td>
</tr>
<tr>
<td>to_user_id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>好友ID</td>
</tr>
<tr>
<td>accept</td>
<td>BOOLEAN</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>接受状态</td>
</tr>
</tbody></table>
</li>
<li><p>chat_history表:存储好友的聊天记录,包括发送方ID(user_id)、接收方(target_id)ID(target_type)、聊天数据(data)(BLOB类型存储二进制大对象,可以实现文件数据的直接存储),sent(用于标识消息是否已发送,若未发送,先存储这个操作,在某一次事件再次触发时检查标志位,操作服务端再次控制client.socket发送消息)。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>数据类型</th>
<th>主键</th>
<th>是否唯一</th>
<th>是否为空</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>消息ID</td>
</tr>
<tr>
<td>user_id</td>
<td>INTEGER</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>用户ID</td>
</tr>
<tr>
<td>target_id</td>
<td>INTEGER</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>消息目标ID</td>
</tr>
<tr>
<td>target_type</td>
<td>TEXT</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>目标类型:群/用户</td>
</tr>
<tr>
<td>data</td>
<td>BLOB</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>消息体</td>
</tr>
<tr>
<td>sent</td>
<td>BOOLEAN</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>发送是否成功</td>
</tr>
</tbody></table>
</li>
<li><p>rooms表:群组表,包括该群组的主键ID(id)、群组的名称(room_name)。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>数据类型</th>
<th>主键</th>
<th>是否唯一</th>
<th>是否为空</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>群聊ID</td>
</tr>
<tr>
<td>room_name</td>
<td>TEXT</td>
<td>N</td>
<td>Y</td>
<td>N</td>
<td>群聊名</td>
</tr>
</tbody></table>
</li>
<li><p>room_user表:群组用户表,包括群组的ID,群聊房间号(room_id)、加入该群组的用户(user_id)。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>数据类型</th>
<th>主键</th>
<th>是否唯一</th>
<th>是否为空</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>INTEGER</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>ID</td>
</tr>
<tr>
<td>room_id</td>
<td>INTEGER</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>群聊ID</td>
</tr>
<tr>
<td>user_id</td>
<td>INTEGER</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>用户ID</td>
</tr>
</tbody></table>
</li>
</ol>
</li>
</ol>
<h3 id="性能需求"><a href="#性能需求" class="headerlink" title="性能需求"></a>性能需求</h3><ol>
<li><p>可靠性需求</p>
<p>保证一个用户只能同时使用一个IP地址登录,客户端不会出现闪退、加密无效的情况。</p>
</li>
<li><p>安全性需求</p>
<p>客户端做好完整的封装;传输信息采用经过公钥加密机制协商的AES对称加密秘钥;服务器及时更新客户端IP地址等信息。</p>
</li>
<li><p>可维护性与可扩展性需求</p>
<p>对于软件功能方面,采用高内聚低耦合的模块化设计,包括登录模块、注册模块、好友列表模块、聊天模块等,确保每个模块的具有较高的独立性,使软件源码便于维护,同时便于后期添加聊天群等更多扩展内容,保证软件可以进行更新换代。</p>
</li>
<li><p>运行环境需求</p>
<ol>
<li><p>客户端:python3</p>
</li>
<li><p>服务端:</p>
<p>python3</p>
<p>PC硬盘容量:50G<br>运行内存:2G</p>
</li>
</ol>
</li>
</ol>
<h3 id="UI需求"><a href="#UI需求" class="headerlink" title="UI需求"></a>UI需求</h3><ol>
<li>页面内容:聊天字体大小和颜色可更改,主题突出,语言简单明了易懂,菜单设置合理、页面布局规范,文字准确,语言流畅。</li>
<li>技术环境:页面大小合适,无错误链接和空链接。</li>
<li>艺术风格:界面版面形象清晰,布局合理,字号大小适宜,字体选择合理,前后一致,动静搭配恰当,色彩和谐自然,与主题内容协调。</li>
</ol>
<h3 id="操作需求"><a href="#操作需求" class="headerlink" title="操作需求"></a>操作需求</h3><ol>
<li>所有弹出的窗口不超过一层,无层层堆叠的现象,不能无故为操作增加复杂度。</li>
<li>用户注册、用户登录、添加好友、删除好友聊天窗口的开启等所有操作务必要简单、快捷,限制在两次点击以内。</li>
<li>考虑到操作人员工作的实际环境状况,就要保证设计的按键足够的清晰足够大。</li>
</ol>
<h2 id="详细设计"><a href="#详细设计" class="headerlink" title="详细设计"></a>详细设计</h2><h3 id="系统结构说明"><a href="#系统结构说明" class="headerlink" title="系统结构说明"></a>系统结构说明</h3><p>本系统的核心控制逻辑在于C-S-C之间发送的数据中包含了操作码,接收方通过对接收码的识别作出规定的操作。例如服务端接收添加好友的操作码会执行add_friend.py。客户端接收操作码并不断把对应函数放入递归函数的队列中,由递归函数逐一执行队列中的函数。</p>
<p>系统主要分为三个部分:</p>
<ul>
<li>聊天客户端(client)</li>
<li>集中服务器(server)</li>
<li>相互通信时的共同部分(common)</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line">Uchat</span><br><span class="line">│ config.json</span><br><span class="line">│ run_client.py</span><br><span class="line">│ run_server.py</span><br><span class="line">│</span><br><span class="line">├─client</span><br><span class="line">││ __init__.py</span><br><span class="line">││</span><br><span class="line">│├─components</span><br><span class="line">││ contact_item.py<span class="comment">//联系人列表UI</span></span><br><span class="line">││ vertical_scrolled_frame.py<span class="comment">//Tkinter可滚动框架</span></span><br><span class="line">││ __init__.py</span><br><span class="line">││</span><br><span class="line">│├─forms</span><br><span class="line">│││ chat_form.py<span class="comment">//聊天界面及处理与聊天相关的事件</span></span><br><span class="line">│││ contacts_form.py<span class="comment">//联系人列表</span></span><br><span class="line">│││ login_form.py<span class="comment">//登录界面</span></span><br><span class="line">│││ register_form.py<span class="comment">//注册界面</span></span><br><span class="line">│││ __init__.py</span><br><span class="line">│││</span><br><span class="line">││└───images<span class="comment">//背景图片</span></span><br><span class="line">││ contacts_bg.gif</span><br><span class="line">││ contacts_bg.png</span><br><span class="line">││ login_bg.gif</span><br><span class="line">││ register_bg.gif</span><br><span class="line">││ VerticalScrolled.png</span><br><span class="line">││</span><br><span class="line">│├─memory</span><br><span class="line">││ __init__.py<span class="comment">//缓存数据</span></span><br><span class="line">││</span><br><span class="line">│└─util</span><br><span class="line">│ │ __init__.py</span><br><span class="line">│ └─ socket_listener</span><br><span class="line">│ __init__.py<span class="comment">//监听socket的线程</span></span><br><span class="line">│</span><br><span class="line">├─common</span><br><span class="line">││ config.py<span class="comment">//获取配置信息</span></span><br><span class="line">││ global_vars.py<span class="comment">//全局变量</span></span><br><span class="line">││ __init__.py</span><br><span class="line">││</span><br><span class="line">│├─cryptography</span><br><span class="line">││ crypt.py<span class="comment">//密钥协商相关函数</span></span><br><span class="line">││ prime.py<span class="comment">//随机生成一个大素数</span></span><br><span class="line">││ __init__.py</span><br><span class="line">││</span><br><span class="line">│├─message</span><br><span class="line">││ __init__.py<span class="comment">//消息处理(消息类型定义,序列化过程等)</span></span><br><span class="line">││</span><br><span class="line">│├─transmission</span><br><span class="line">││ secure_channel.py<span class="comment">//安全信道的建立和传输</span></span><br><span class="line">││ __init__.py</span><br><span class="line">││</span><br><span class="line">│└─util</span><br><span class="line">│ │ __init__.py</span><br><span class="line">│ │</span><br><span class="line">│ └─ socket_listener</span><br><span class="line">│ __init__.py<span class="comment">//客户端socket监听</span></span><br><span class="line">│</span><br><span class="line">└─server</span><br><span class="line"> │ database.db</span><br><span class="line"> │ main.sql</span><br><span class="line"> │ __init__.py</span><br><span class="line"> │</span><br><span class="line"> ├─broadcast</span><br><span class="line"> │ __init__.py<span class="comment">//广播消息</span></span><br><span class="line"> │</span><br><span class="line"> ├─event_handler</span><br><span class="line"> │ add_friend.py<span class="comment">//加好友</span></span><br><span class="line"> │ bad.py<span class="comment">//出现错误操作使程序走向可处理除0操作</span></span><br><span class="line"> │ client_echo.py<span class="comment">//测试CS通信</span></span><br><span class="line"> │ create_room.py<span class="comment">//创建群聊</span></span><br><span class="line"> │ del_friend.py<span class="comment">//删除好友</span></span><br><span class="line"> │ join_room.py<span class="comment">//加入群聊</span></span><br><span class="line"> │ login.py<span class="comment">//登录</span></span><br><span class="line"> │ query_room_users.py<span class="comment">//执行群聊中发消息的操作</span></span><br><span class="line"> │ <span class="keyword">register</span>.py<span class="comment">//注册</span></span><br><span class="line"> │ resolve_friend_request.py<span class="comment">//处理加好友请求</span></span><br><span class="line"> │ send_message.py<span class="comment">//发消息</span></span><br><span class="line"> │ __init__.py</span><br><span class="line"> │</span><br><span class="line"> ├─memory</span><br><span class="line"> │ __init__.py<span class="comment">//缓存数据</span></span><br><span class="line"> │</span><br><span class="line"> └─util</span><br><span class="line"> │__init__.py<span class="comment">//添加对象类型</span></span><br><span class="line"> │</span><br><span class="line"> └─database</span><br><span class="line"> __init__.py<span class="comment">//数据库操作</span></span><br></pre></td></tr></table></figure>
<ol>
<li><p>聊天客户端client</p>
<p>实现安全即时通信系统的客户端,主要功能是通过界面与用户实现交互;通过socket与集中服务器进行通信,获得集中服务器的服务,实现用户的注册登录等功能。与好友即时通信和加入群聊通信。</p>
<ol>
<li>登录模块LoginForm<br>该模块创建登录界面并可链接到注册界面,若输入为空则报错,否则将获取用户输入的用户名和密码打包成登录请求消息(MessageType.login)发送给服务器,服务器根据消息类型和数据包中的内容以及在数据库查找到的结果进行判断,根据不同情况发送不同的反馈给客户端。客户端收到反馈消息,若data[‘type’]为login_failed,则用户名和密码输入有误;若为data[‘type’]为login_successful则根据memory进入登录后显示好友列表的ContractsForm界面。</li>
<li>注册模块RegisterForm<br>该模块只有在登录界面点击注册按钮时才会显示。通过注册窗口获得用户输出的个人信息:用户名、密码、邮箱、性别、年龄,若其中用户名、邮箱、密码为空或两次密码输入不一致则会提示相应的错误以引导用户进行正确的输入,否则将获取用户输入打包成注册请求消息(MessageType.register)发送给服务器。服务器查找数据库判断是否用户名已经注册过,发送不同反馈给用户,若data[‘type’]为username_taken,则用户名已被注册,若data[‘type’]为MessageType.register_successful则注册成功,并且在客户端生成证书包含用户的用户名,邮箱,公钥。</li>
<li>主界面ContactsForm<br>该模块在用户登录成功以后显示。显示好友列表中好友的用户名、在线状态,ip地址及端口号等。下方的按钮有添加好友、删除好友、添加群聊、创建群聊。可以点击相应的按钮进行相应的操作,发给服务器相应的数据包,服务器接收到数据包后进行解析,根据不同类型进行event_handler。若点击好友列表或群聊即可跳出聊天界面进行聊天。未读的消息会用红点标注,根据最后一条消息的发送时间来对好友列表排序。</li>
<li>.聊天界面ChatForm<br>该模块是用户与好友聊天的界面。当用户在好友列表中点击好友列表时,即向好友发出聊天,服务器收到聊天请求后寻找对方的证书,找到对方的公钥,双方使用Diffie-Hellman算法协商算法,然后利用AES加密消息保证消息的机密性,MD5生成消息摘要验证保证消息的完整性。另外还可以更改聊天过程中字体的大小和颜色,支持多行输入,支持发送文件,以图片为例,将保存聊天过程中的接收到的图片,并识别其格式。</li>
<li>客户端部件(components)模块<br>该模块实现tkinter静态部件添加和滚动模块的实现。</li>
<li>客户端memory管理模块(memory)<br>该模块用于初始化tkinter对象tk的属性,如窗口,secure_channel对象等。</li>
<li>多用(util)中的socket_listener模块<br>该模块用于客户端处理消息类型,文本或其他。以及不断循环建立连接socket接收消息,实现数据包的完整接收。定义处理给好友框,消息框更新历史消息的函数,事件操作的监听函数和移除函数,消息的监听函数和移除函数。可以实现接收数据并且拼成块,更新聊天的历史记录,通知客户端更新contacts界面上的最后一条消息的内容,时间,未读消息的数目等。</li>
</ol>
</li>
<li><p>集中服务器server</p>
<ol>
<li>event_handler模块<br>该模块分为11个部分,分别具体处理客户端发来的各个操作事件。如登录加好友删除好友等操作。</li>
<li>database数据库模块<br>该模块主要是根据客户端触发的事件对数据库的各种操作。</li>
<li>broadcast广播模块<br>该模块主要是针对群聊,为群组中的每个在线用户广播发送消息。</li>
<li>memory模块<br>定义用户与secure_channel对象互相映射的字典列表,所有已经建立的secure_channel对象,以及用户下线后将其从在线secure_channel对象列表中移除的操作。</li>
</ol>
</li>
<li><p>客户端服务器公用模块common</p>
<ol>
<li>message模块<br>将变量数据等变成可存储或者传输的过程即序列化,同时还将各个事件的类型变为枚举变量保存,将收到的数据包反序列化进行恢复,再提取数据包中Message的类型。</li>
<li>cryptography模块<br>用于调用其生成公钥,然后从证书中获取公钥,再使用D-H算法协商生成共享密钥。</li>
<li>prime模块<br>该模块主要是判断是否为素数,生成大素数,为证书的生成提供基础。</li>
<li>secure_channel模块<br>该模块主要是定义secure_channel类,即包裹了socket和参数秘钥的封装对象。在通信对象之间协商好对称加密秘钥之后封装在这个新的对象中。并且这个对象对数据有新的函数功能:<ul>
<li>send函数<br>用于对发送的序列化之后的数据用对称加密秘钥进行AES加密并用struct结构体将其打包成自设协议格式的数据包。</li>
<li>on_data<br>函数主要用于接受数据的逆向解析。按照要求配置socket和数据传输的格式进行规则化。数据包的格式为前四个字节为消息体的长度,接着一字节存储AES加密时的消息填充长度,然后16字节AES加密时所需要的初始值,再接着是32字节的消息摘要,最后才是加密后的消息体。</li>
</ul>
</li>
</ol>
</li>
</ol>
<h3 id="重要数据说明"><a href="#重要数据说明" class="headerlink" title="重要数据说明"></a>重要数据说明</h3><ol>
<li><p>发送接收的数据格式</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714133500.png"></p>
</li>
<li><p>接收数据时的三个字典</p>
<ol>
<li>bytes_to_receive={}——用于存储对应用户id或者服务器的将要接收的数据;</li>
<li>bytes_received={}——用于标识已经接收的数据;</li>
<li>data_buffer={}——用于将已经接收的数据解密,反序列化生成最初的数据字符串。</li>
</ol>
</li>
<li><p>全局都在引用的数据</p>
<ol>
<li>sc_to_user_id={}——表示映射关系为sc->user_id的字典;</li>
<li>user_id_to_sc={}——表示映射关系为user_id->的字典;</li>
<li>socket_to_sc={}——表示socket和已生成sc对象的映射关系字典;</li>
<li>scs=[]——存储所有运行出来的sc(secure_channel)对象;</li>
<li>chat_history=[]——用于暂时存储聊天信息历史。</li>
</ol>
</li>
<li><p>客户端接收到的数据data</p>
<p>data是一部字典,它包括key:parameters,type.parameters也是一部字典,内部包括key:target_type,time,sender_id(发送者id),target_id(接收方id),sender_name(接收者姓名,message字典(内含数据内容,字体,字体大小颜色)。而外层的这个type存储的是交给server的MessageType类型,如果是不同的MessageType会进行不同的数据库操作和客户端操作。具体实例如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">data={</span><br><span class="line"> 'parameters':{</span><br><span class="line"> 'target_type':0,</span><br><span class="line"> 'time':1562754761321,</span><br><span class="line"> 'sender_id':1,</span><br><span class="line"> 'target_id':2,</span><br><span class="line"> 'sender_name':'1',</span><br><span class="line"> 'message'{</span><br><span class="line"> 'data':'hello',</span><br><span class="line"> 'fontsize':10,</span><br><span class="line"> 'type':0,</span><br><span class="line"> 'fontcolor':'#000000'</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> 'type':<MessageType.on_new_message:109></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>例子中data[‘parameters’][‘target_type’]=0表示文本信息,从id为1的用户发出信息,发给id标号为2的人,发送方昵称为‘1’。给服务器发送的操作码为109。</p>
</li>
</ol>
<h3 id="安全传输"><a href="#安全传输" class="headerlink" title="安全传输"></a>安全传输</h3><ol>
<li><p>数据包结构</p>
<ol>
<li><p>消息加密算法:AES对称加密算法,保证消息机密性</p>
</li>
<li><p>消息摘要算法:MD5算法,保证消息的完整性</p>
</li>
<li><p>包结构分析</p>
<ol>
<li><p>第一层(解密前)</p>
<p>通过函数struct.pack()构造加密的数据包,结构如下</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134257.png"></p>
<p>相关代码如下</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">iv1=<span class="built_in">bytes</span>(os.urandom(<span class="number">16</span>))</span><br><span class="line"> data_to_encrypt=serialize_message(message_type,parameters)</span><br><span class="line">length_of_message=<span class="built_in">len</span>(data_to_encrypt)</span><br><span class="line">padding_n=math.ceil(length_of_message/<span class="number">16</span>)*<span class="number">16</span>-length_of_message</span><br><span class="line"> foriinrange(<span class="number">0</span>,padding_n):</span><br><span class="line"> data_to_encrypt+=<span class="string">b'\0'</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>其中iv1是16字节随机数作为初始向量。要加密的数据是序列化的初始数据。获取长度后用\0填充。然后将数据用python库函数aes,cbc模式加密得到加密数据。</p>
</li>
<li><p>第二层(解密后)</p>
<p>结构如下</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134437.png"></p>
<ul>
<li><p>MessageType:event_handler_map()规定的操作码</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134505.png"></p>
</li>
<li><p>Parameter:字典参数,包含target_type(标志群聊或者私聊)等</p>
</li>
</ul>
</li>
<li><p>第三层,序列化数据的安排格式</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134543.png"></p>
</li>
<li><p>第四层,基础数据部分</p>
<p>包括int、str、bool、float、binary等</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134625.png"></p>
</li>
</ol>
</li>
</ol>
</li>
<li><p>密钥分发</p>
<ol>
<li><p>协商过程</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134751.png"></p>
<p>客户端的证书、公钥、私钥</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134808.png"></p>
</li>
<li><p>加密算法</p>
<p>采用DH协商对称加密的共享密钥,具体过程如下</p>
<p><img src="https://raw.githubusercontent.com/Leeyuxun/pic-storage/main/img/20200714134732.png"></p>
</li>
</ol>
</li>
</ol>
<h3 id="程序函数清单"><a href="#程序函数清单" class="headerlink" title="程序函数清单"></a>程序函数清单</h3><h4 id="客户端函数"><a href="#客户端函数" class="headerlink" title="客户端函数"></a>客户端函数</h4><ol>
<li><p>socket_listener(self,data)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/register_form.py</li>
<li>client/forms/login_form.py</li>
<li>client/forms/contacts_form.py</li>
<li>client/forms/chat_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在的类的自身</li>
<li>data:监听数据</li>
</ul>
</li>
<li><p>功能</p>
<p>在注册、登录、好友列表、聊天框四个页面建立事件监听,解析监听data,确定数据中MessageType的类型,在register_form.py文件中用于判断用户名是否被占用、返回注册结果;在login_form.py中用于返回登录结果;在contacts_form.py文件中用于判断是否处理添加、删除好友、添加、创建群聊以及执行操作的结果、判断好友是否下线并刷新好友列表。</p>
</li>
</ul>
</li>
<li><p>remove_socket_listener_and_close(self)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/register_form.py</li>
<li>client/forms/login_form.py</li>
<li>client/forms/contacts_form.py</li>
<li>client/forms/chat_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在的类的自身:RegisterForm、LoginForm、ContactsForm、ChatForm</li>
</ul>
</li>
<li><p>功能</p>
<p>通过调用util/socket_listener文件下的remove_listener函数来关闭事件监听,同时调用库函数destroy()关闭窗口、清空客户端缓存信息。</p>
</li>
</ul>
</li>
<li><p>_<em>init</em>_(self,master=None)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/register_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在类RegisterForm自身</li>
<li>master:注册主窗口用来容纳其他组件,默认一个窗口master=None</li>
</ul>
</li>
<li><p>功能</p>
<p>通过库函数super()实现子类__init__()对父类__init__()的继承;对注册窗口进行布局,包括确定注册界面的长宽,确定背景、标签、输入框、按钮等的位置、颜色、类型、链接等;初始化安全信道;通过socket_listener()函数和remove_socket_listener_and_close()函数控制对客户端socket事件监听和关闭。</p>
</li>
</ul>
</li>
<li><p>do_register(self)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/register_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在类RegisterForm自身</li>
</ul>
</li>
<li><p>功能</p>
<p>检查输入的用户名、密码、邮箱是否合法;判断两次输入的密码是否相同;调用get_ip()函数获取客户端的IP地址和端口号;向服务器发送注册请求,并通过调用函数send()将注册输入的用户名、密码、邮箱、性别、年龄以及用户的IP地址和端口号等信息发送给服务器;构造数字证书,命名为IP地址+“——cert.pem”,内容为用户名+邮箱+用户公钥</p>
</li>
</ul>
</li>
<li><p>._<em>init</em>_(self,master=None)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/login_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在类RegisterForm自身</li>
<li>master:登录主窗口用来容纳其他组件,默认一个窗口master=None</li>
</ul>
</li>
<li><p>功能</p>
<p>通过库函数super()实现子类__init__()对父类_<em>init</em>_()的继承;对登录窗口进行布局,包括确定注册界面的长宽,确定背景、标签、输入框、按钮等的位置、颜色、类型、链接等;初始化安全信道;通过socket_listener()函数和add_listener()函数将服务器端加入到监听列表中。</p>
</li>
</ul>
</li>
<li><p>do_login(self)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/login_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>参数self:所在类LoginForm自身</li>
</ul>
</li>
<li><p>功能</p>
<p>检查输入的用户名、密码是否合法;通过调用函数send()向服务器发送登录请求,并将输入的用户名、密码等信息发送给服务器。</p>
</li>
</ul>
</li>
<li><p>show_register(self)</p>
<ul>
<li><p>位置</p>
<ul>
<li>client/forms/login_form.py</li>
</ul>
</li>
<li><p>参数</p>
<ul>
<li>self:所在类LoginForm自身</li>
</ul>
</li>
<li><p>功能</p>
<p>与注册按钮关联,通过点击按钮调用库函数Toplevel()跳转到注册页面。</p>
</li>
</ul>