• Stars
    star
    113
  • Rank 310,115 (Top 7 %)
  • Language
    Java
  • License
    MIT License
  • Created almost 8 years ago
  • Updated about 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple Java talk software.

Talk

A simple Java talk software.

这是一个Java聊天系统,作为Java实验课的内容,目前已基本完成,支持如下功能:

  • 群聊
  • 私聊
  • 消息提醒
  • 用户状态标记
  • 聊天记录保存
  • 表情支持

效果如下图:

这是私聊的界面,其中可以看到Master,表示群聊大厅,选中可以进行群聊,而选择其他用户,则表示私聊。

名字后面的(*)表示消息提醒,切换标签即可查看,而(Offline)则标记用户已经离线。

![私聊](images/Screenshot from 2016-12-15 20-22-33.png)

同样,可以发送表情,不那么单调。

![表情支持](images/Screenshot from 2016-12-15 20-24-53.png)

这是服务端界面,主要是记录用户的登入、注销。

![服务端界面](images/Screenshot from 2016-12-15 20-49-07.png)

下面我就来写下我的设计思路吧。

设计思路

首先我要吐槽的是,界面真难写,从代码统计中可以看出,我的服务端230行左右,而客户端达到700多行,同时客户端也写的一坨,纯粹是面向过程的写法了。

这里我把客户端和服务端写到一个项目里(有3个包,一个客户端,一个服务端,一个公用数据),通过命令行参数来判断是启动客户端,还是服务端--server,同时互不依赖。

在写客户端的过程中,发现Swing比较丑,查了下资料,最后选择了JavaFX来构建界面。

刚开始是用openjdk来编写,发现没有内置JavaFX库,最后还是老老实实用了Oracle-JDK

期间也查了不少资料,全是关于客户端的细节处理。

公用数据包

这里主要定义了3个类,分别介绍如下。

Talk类就是程序的主入口了,通过判断是否带--server来启动服务端或者客户端。

TalkUser类,主要是为服务端使用的,标记了用户名userName,以及该用户收到的消息队列message(为[*FROM <from>]格式,后面会讲)。同时也定义了sendMsg(存储用户消息)和sendAll(存储群聊消息)方法,来存储消息。可能方法名有点误导,比如说调用usr.sendMsg(from, msg),其实是usr存储from发来的消息,而不是发送消息= =。sendAll类似。

TalkEmoji类,这个类比较智障,存储了各个Emoji表情的Unicode码,这里提前说下,其实早在2010年,Unicode编码就已经纳入了700多个Emoji表情,所以是可以支持表情的,只要加载支持Emoji表情的字库即可。参考链接:How to support Emojis

emoji

服务端pers.netcan.talk.server

服务端接口设计,比较烂大街(传统)的Master-Worker模型,设置一个Master主线程,专门用来监听客户端请求;当客户端请求时,则创建一系列子线程Workers来处理各个客户端的请求。

需要注意的是Java很容易产生Null指针异常操作问题,这里要仔细处理。

之后就是设计一套专用的协议,以实现服务端与客户端的交互。

我设计的协议如下:

  • 客户端请求

    • [REGISTER]<username>: 用户注册到服务器,服务器产生一个子线程专门来处理这个用户的请求
    • [GETUSRS]: 服务端返回在线用户列表[USERS]<uesr1>, <user2>...,用逗号隔开
    • [SENDTO <to>]<message>: 用户发送message消息给名为to的用户,若发送给Master,则[ALLFROM]响应。
    • [LOGOUT]:用户注销
  • 服务端响应

    • [USERS]<uesr1>, <user2>...: 响应客户端的[GETUSRS]请求,返回各个在线用户名
    • [OK]: 目前仅表示用户的[REGISTER]请求成功,即登录成功
    • [FAILED]: 目前仅表示用户的[REGISTER]请求失败,即登录失败,可能因为重名。
    • [FROM <from>]<message>: 表示当前用户收到一个名为from用户的message消息。
    • [ALLFROM <from>]<message>: 表示当前用户收到一个名为from用户的群发(在Master标签中显示)message消息。

好吧,应该就那么几条指令,这样对于一个聊天系统来说足够了,需要注意的是发送的时候,用flush方法立即将发送缓冲区中的内容发送出去,而不是等到缓冲区满了才发送,这样就没有交互性可言了。

忘记说一点,我是这么处理用户的消息的,在Master中有一个Users全局变量(这样所有的线程都能访问了),它的类型为<TalkUser>,就是前面公用数据包中提到的数据结构,每当接收到用户的发送指令[SENDTO]时,就调用对应用户的sendto方法来存储消息到自己的消息队列中。而每个Worker线程,都会在300ms内检查各自处理用户的消息队列是否有消息,一有就立刻发送给对应的客户端,让客户端展示出来。

之前和一个同学讨论这个聊天软件是怎么设计比较合适,他比较纠结一个问题,就是怎么调度各个用户发送的消息,所以考虑用轮询的做法,而我一开始就没考虑过这个问题,因为很简单啊,用户A发消息给用户B,用户B直接展示出来就行了,反过来类似,如果同时发,怎么调度?当然是谁网速快就先处理谁的= =,同理,群聊也是,服务器先收到谁的,就立刻发送给各个用户,先后顺序完全由发送时间和网速来确定,所以不用考虑那么多的。(当然可能每个用户的消息记录显示顺序不一样,这也是有可能的)。

客户端pers.netcan.talk.client

客户端写的就比较凌乱了,它的职责无非就是解析服务器响应,展现给用户,同时将用户的操作(主要是发送命令)发送给服务器处理。

看起来比较容易,细节还是比较难处理的。客户端开一个线程,每300ms发送一个[GETUSRS]报文给服务器,服务器响应报文,返回用户列表,也就是说每300ms刷新一下用户列表,这里起到2个作用,一是相当于心跳包,维持TCP长连接,二是实时获取在线用户,之后就是接收消息,每300ms接收一条消息是可以接受的。

然而这个专门用来刷新消息的线程,若修改UI会出错,无奈查了大量资料,用Task<Void>来处理,将修改UI、刷新消息部分代码放到如下代码块中处理。

Platform.runLater(new Runnable() {
	@Override
	public void run() {
		...
	}
});

用户状态标记,这里当用户离线的时候,就加个(Offline)标记,有新消息,就加个(*)标记,用正则表达式"([\\w\\d]+)( \\((\\*|Offline)\\))?"来匹配是哪种状态,看起来够难写的。

发送消息,响应发送按钮点击事件,和回车事件,然后将发送框中的消息<msg>,用户列表选中的用户<to>,发送[SENDTO <to>]<msg>指令给服务端。需要注意的是,为了减少特殊字符(例如换行)带来的麻烦,将消息字符串利用base64编码再发送,接收的时候再base64解码,就不用考虑那么多文本处理细节了。

接收消息,每300ms响应一下服务端,然后检查是否有[*FROM]响应,并将消息存储至消息记录中。客户端展现出来。

表情支持,将一些Emoji表情的Unicode码存到按钮中,然后响应按钮事件,点击按钮就把表情附加到发送框中,这里又出现一个问题,我将emojis定义为一个按钮数组,那么绑定事件会出现问题:

for(int i=0; i<TalkEmoji.emoji.length; ++i) { // 将表情显示到按钮上
	emojis[i].setOnAction((event) -> {
			sendMsg.appendText(emojis[i].getText());
	});
}

将编译不过去,因为eventlambda表达式引用了i这个外部变量,这在Java中是不允许的(只能将外部变量声明为final),this我也想过了,不行,没办法,又查了大量资料,解决如下:

((Button) event.getSource()).getText()

利用event.getSource()方法获取是哪个对象响应的事件。

当用户点击退出按钮的时候,就将内存中的聊天记录以用户名为文件名的方式保存到文件中,登录的时候加载一下文件的内容到内存中即可。

还有一点要注意的是,保存/读取文件需要指定编码,否则在Win平台下运行,保存/读取的内容将乱码。

TODO

  • 服务器接口
  • 完成客户端
  • 聊天记录保存至文件
  • 增加表情支持

More Repositories

1

asyncio

asyncio is a c++20 library to write concurrent code using the async/await syntax.
C++
808
star
2

compilingTheory

My course design for compiler theory (Visualization).
C++
284
star
3

config-loader

Simple C++ Config Loader Framework(Serialization & Reflection)
C++
211
star
4

Laravel_AJAX_CRUD

A Laravel application for AJAX CRUD page.
PHP
119
star
5

recipes

There is my code snippet.
C++
109
star
6

AnimalChess

Animal Fight Chess Game(斗兽棋) written in rust.
Rust
90
star
7

2017-HUAWEI-Codecraft

2017华为软件精英挑战赛,上合赛区,围墙编队
C++
88
star
8

Leetcode-Rust

My solutions for leetcode by rust lang.
Rust
68
star
9

HFUT_ChemLab

A Laravel application for HFUT Chemistry lab learning and exam system.
PHP
66
star
10

SlidePuzzle

Slide puzzle game written by Netcan. using the SDL engine and use of A* algorithm.
C++
62
star
11

HFUT_Thesis

合肥工业大学毕业设计(论文)模板
TeX
59
star
12

baike_contest

合工大宣百科竞赛平台 By Netcan
PHP
56
star
13

MyCrawler

我的爬虫合集
Python
55
star
14

HFUT_Market

A databases course design, using python.
Python
53
star
15

advanced-cpp20-programming

机工社《高级C++20编程》随书代码
C++
52
star
16

meta-list

Intuitive & Powerful C++20 consteval metaprogramming library(via value).
C++
47
star
17

ChineseChess

ChinessChess game written by rust.
Rust
24
star
18

LinAlg

实现一个线性代数库,为Python写扩展。《程序猿的数学3 线性代数》读后笔记
C++
20
star
19

NetcanOS

Netcan OS is an operation system for x86 PCs, for learning how os works.
C
13
star
20

gameOfLife

game of life using Javascript and canvas.
JavaScript
9
star
21

2018-HUAWEI-Codecraft

2018华为软件精英挑战赛。
C++
6
star
22

wordStatistics

A tool use for figuring word's frequency.
C++
5
star
23

FCEmulator

There is my graduation project, a FC Emulator.
C++
4
star
24

LeetCode

My Code for leetcode
C++
4
star
25

2017-HIKVISION-CodeChallenge

2017海康威视软件精英挑战赛
C++
3
star
26

qbittorrent-web-control

qbittorrent-web-control, developing under the guidance of AI.
TypeScript
3
star
27

Netcan_ICPC

The Project is Netcan's Programming Contest Experience
C++
3
star
28

LoveCalendar

A calendar to mark something.
Python
3
star
29

HFUT_Live

校内直播平台。
PHP
3
star
30

nano-caf

C++
2
star
31

presentation

My presentation collections for talks.
HTML
2
star
32

netcan

self profile
Python
2
star
33

boost-cmake

CMake
2
star
34

Tictactoe

A Python game, for network programming.
Python
2
star
35

netcan.github.io

My blog for something about programming.
SCSS
2
star
36

HFUT_LabReport

合肥工业大学LaTeX模板
TeX
2
star
37

compute-3d

Exploring rendering techniques for 3D graphics
C++
2
star
38

libnetcan-soft.so

Written in junior high school. on birthday 2012-05-23
C
1
star
39

TianMen_Weather

天门气象公众号开发。
Python
1
star
40

computerOrganization

计算机组成原理课设
Assembly
1
star