d0304 更新功能实现
d0312 更新部分图片&UI设计部分
d0318 更新功能实现
d1222 实现添加好友功能、实现注册功能、修改大量BUG
github:https://github.com/He11oLiu/ChatRoom.git

==========================================================

javafx聊天室实现带界面 java聊天室的设计与实现_javafx聊天室实现带界面

即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流。如QQ,微信都属于即时通讯。

本篇博客将详细记录我在JAVA上搭建一个自己的即时通讯工具的实现方法,如有错误的地方或者建议,请多多提出。

功能实现

基本技术

  • Socket 与 ServerSocket
    作为一个即时通讯工具,客户端(Client)和服务器(Server)是两个必不可少的部分。首先我们就来解决服务器和客户端连接的问题。

Socket的英文原义是“孔”或“插座”。
Socket用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。

  • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。建立了一个Socket后,从这个Socket中读取I/O流,就可以实现两个程序的通讯。
    而服务器需要与多个客户端进行双向的通讯连接,并非单一的连接。于是可以通过每接收到一个Client后就开一个单独的进程,来完成与该Client的通讯步骤。这样就可以同时接受多个Client,从而实现服务器的功能。
  • javafx聊天室实现带界面 java聊天室的设计与实现_客户端_02

  • 客户端建立:
    Java提供了Socket类,用来建立客户端
    提供的构造方法如下:
  • javafx聊天室实现带界面 java聊天室的设计与实现_服务器_03

  • 当调用构造方法,构造一个新的Socket的时候,就是向指定的端口请求连接。
    通过以下方法,可以获取该Socket的I/O流
  • javafx聊天室实现带界面 java聊天室的设计与实现_客户端_04


  • javafx聊天室实现带界面 java聊天室的设计与实现_好友列表_05

  • 通过close方法,关闭该Socket
  • javafx聊天室实现带界面 java聊天室的设计与实现_服务器_06

  • 服务器建立:
    Java提供了ServerSocket类,用来建立服务器。
    提供的构造方法如下:

    可以通过Telnet来测试是否成功的建立了服务器。(win7后默认关闭,在控制面板-程序-启用或关闭windows功能下开启)
telnet localhost <port>

或者

telnet 127.0.0.1 <port>

该类下自带了

javafx聊天室实现带界面 java聊天室的设计与实现_好友列表_07


accept方法用来接收连接,返回一个Socket对象。接收该对象,并调用刚才提到的getInputStream()方法以及getOutputStream()方法,即可实现获取该Socket的I/O流。从该流上读取信息或者写入信息,即可达到通讯的目的。要注意的是,此时客户端的Input流对应着服务器端的Output流。

同样ServerSocket也带了close方法,用来关闭服务器。

javafx聊天室实现带界面 java聊天室的设计与实现_好友列表_08

  • 通讯协议设计

通讯协议具体内容

type


描述


1.注册请求信息

0x01

客户发送注册请求数据给服务器

MsgReg

2.注册应答消息

0x11

客户端返回注册结果

MsgRegResp

3.登陆请求信息

0x02

客户端发送包括JK号及密码

MsgLogin

4.登录应答消息

0x22

服务器返回登录结果

MsgLoginResp

5.添加好友信息

0x05

客户端向服务器发送添加好友JK号和添加好友所在列表

MsgAddFriend

6.添加好友应答信息

0x55

服务器返回添加好友结果

MsgAddFriendResp

7.好友列表

0x03

服务器给用户发送好友列表信息

MsgTeamList

8.聊天信息

0x04

客户端给服务器/服务器给客户端消息

MsgChatText

消息头(MsgHead)

MsgHead

消息头(13)

消息体

int totalLen

消息总长度(4)

byte type

消息类型(1)

int desk

目标JK号(4)

服务器的JK号:2000000000

int src

发送用户JK号(4)

登录发送JK号:2000000001

注册消息体(MsgReg extends MsgHead)

成员属性

类型

长度(总长33)

String nikeName

Octet String

10

昵称

String Pwd

Octet String

10

密码

注册应答消息(MsgRegResp)

成员属性

类型

长度(总长14)

byte state

byte

1

若state为0.desk为目标JK号;若state为1(或其它)错误

登录请求信息(MsgLogin)

成员属性

类型

长度(总长23)

String pwd

Octet String

10

密码JK号保存到src中

登录应答信息(MsgLoginResp)

成员属性

类型

长度(14)

byte state

byte

1

若为0:登录成功;若为1:JK/pwd错误;若为2:ip错误;其它:未知错误

添加好友信息 (MsgAddFriend)

成员属性

类型

长度(27)

int add_ID

int

4

所添加好友的JK号

String list_name

Octet String

10

添加好友至好友列表的名称

添加好友应答信息 (MsgAddFriendResp)

成员属性

类型

长度(14)

byte state

byte

1

若为0:添加好友成功;若为1:不存在该用户;若为2:已经存在该好友;若为3:创建好友列表失败

好友列表信息(MsgTeamList)

成员属性

类型

长度(总长:利用计算长度)

String UserName

Octet String

10

用户名字

int UserPic

int

4

用户头像

byte listCount

byte

1

好友分组个数,表示有几组

String listName

Octet String

10

分组名称

byte bodyCount

byte

1

本组有多少个用户

int bodyPic

int

4

好友头像

int bodyNum

int

4

好友的JK号

String nikeName

Octet String

10

好友

byte bodyState

byte

1

好友状态,0:在线

聊天信息(MsgChatText)

成员属性

类型

长度

String msgText

Octet String

13

可聊天的内容

发送一次信息的总流程

javafx聊天室实现带界面 java聊天室的设计与实现_服务器_09

框架设计

界面层

ChatRoom的主要界面有:

  • 登陆界面
  • 注册界面
  • 好友列表界面
  • 添加好友界面
  • 聊天界面
业务逻辑层
  • 服务器业务逻辑层
    服务器主要提供以下功能
    服务器主线程:

功能

创建服务器

描述

创建服务器。

动作

根据指定的port创建服务器。

输入

port(端口)

服务器对应每一个连接的单独线程ServerThread
这些ServerThread利用HashMap保存到线程库,key为JK号

功能

接受Client

描述

接受客户端,创建单独线程,并通过输入输出流通信。

动作

循环监听是否有client接入,若有,则创建单独线程对其进行操作。

相关

服务器服务线程,ServerThread


功能

响应登陆请求&发送好友列表

描述

响应客户端发送的登陆请求。

输入

根据输入流中读取的数据以及通讯协议,读取userid和pwd。

行动

利用数据库查询输入的userid和pwd是否匹配

输出

根据通讯协议,向输出流输出回执信息,包含最终匹配结果。

行动

若登陆信息匹配,更新用户在线表。

输出

若登陆信息匹配,则再根据通讯协议,向输出流发送好友列表。

相关

数据库,通讯协议


功能

一对一聊天

描述

响应客户端的聊天请求,并将聊天信息发到指定客户端。

输入

根据输入流中读取的数据以及通讯协议,获取目标userid以及发送内容。

行动

根据目标userid以及发送内容,给对应客户端发送信息。

输出

通过寻找对应客户端所接入的thread,给对应客户端发送内容。

相关

通讯协议,serverthread


功能

注册用户

描述

响应客户端的注册请求,并利用数据访问层提供接口写入数据库

输入

根据输入流中读取的数据以及通讯协议,获取目注册用户名以及登陆密码

行动

利用数据访问层提供接口写入数据库

输出

若注册成功,返回客户端住车好的JK号

相关

通讯协议,serverthread


功能

添加好友

描述

响应客户端的添加好友,利用数据访问层提供接口。

输入

客户端发送的添加好友的JK号已经好友列表名称

行动

利用数据访问层提供的接口判断输入的正确性,若正确在数据库中好友列表添加内容

输出

添加好友的状态。

相关

通讯协议,serverthread


  • 客户端业务逻辑层

功能

连接服务器

描述

客户端连接服务器,确认服务器可以连接。

输入

读取类中的ServerIP和port。

动作

根据ServerIP和port,开启Socket,获取输入输出流。

输出

能否连接到服务器


功能

注册

描述

输入用户名和密码

输入

NikeName和Password

动作

将注册请求,打包成消息,发送给服务器。

输出

根据服务器返回结果显示JK号或者显示注册失败。


功能

登陆服务器

描述

客户端申请登陆,确认用户密码是否正确。

输入

用户输入的userid和password

动作

将userid和password根据通讯协议传输到服务器,并接收服务器回信。

输出

用户名密码是否正确

相关

通讯协议


功能

获取好友列表

描述

完成登陆后,从服务器获取好友列表,并显示出好友界面

动作

从服务器获取好友列表,并通过给好友列表界面对象。

相关

通讯协议,好友列表界面对象


功能

一对一聊天

描述

在好友列表中点击一个好友,开始一对一聊天,可以发送/接收信息

动作

根据通讯协议,向指定用户发送信息

输入

发送的目标userid以及发送内容(聊天界面传入)

动作

根据通讯协议,读取信息来源用户,通过聊天界面显示内容。

输出

聊天内容传给聊天界面对象

相关

通讯协议,聊天界面对象


功能

添加好友

描述

添加好友到列表

输入

好友的JK号和列表名

动作

将请求打包成消息,发送给服务器,等待服务器返回结果

输出

根据结果弹出结果消息框,更新好友列表。

数据访问层

本部分由黄成越同学合作完成(https://hcyue.me/)

本次数据库选用SQLite数据库,数据库结构如下。

javafx聊天室实现带界面 java聊天室的设计与实现_客户端_10

数据访问层提供UserModel,其中包括

  • 根据JK号获取用户内容
  • 验证用户信息
  • 获取用户好友列表
  • 增加好友,删除好友

UI设计

  • Metro UI

Metro UI是基于瑞士平面设计原则,其最初在Windows XP的Windows Media Center就中有体现,这有利于以文字为主的界面导航。2006年著名的Zune播放器开始使用类似Metro的设计风格。微软的设计师计划重新设计现有用户界面、更清爽的排版和较少的重点以便于用户使用。Zune的桌面客户端程序也使用了不同于以往Portable Media Center用户界面,其清爽排版和设计给用户耳目一新的冲击。
Metro UI的特点:强调信息本身
参考资料:

javafx聊天室实现带界面 java聊天室的设计与实现_客户端_11

  • 窗体设置无边框以及拖动选项
    为了彻底改变Swing的风格,实现Metro UI的界面,将原有的边框以及按钮重写是必不可少的。
    首先,我们将窗体的边框取消
setUndecorated(true); //设置无边框

这行代码的作用,就是去掉了整个窗体的边框,但是同时也就去掉关闭按钮,缩小放大按钮。同时无法移动边框。为了能够移动边框,我们在窗体上添加一个监听器,代码如下。

addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        isDraging = true;
        wx = e.getX();
        wy = e.getY();
    }

    public void mouseReleased(MouseEvent e) {
        isDraging = false;
    }
}); //确定鼠标按下的位置

addMouseMotionListener(new MouseMotionAdapter() {
    public void mouseDragged(MouseEvent e) {
        if (isDraging) {
            int left = getLocation().x;
            int top = getLocation().y;
            setLocation(left + e.getX() - wx, top + e.getY() - wy);
        }
    }//更改窗体的位置
});
  • 关闭、缩小、选择按钮的重写
    因为选择了无边框,则原来的关闭以及缩小按钮都已经不能使用了,于是可以继承JButton,自己重写一个关闭,缩小按钮。同时为了配合Metro UI,选择按钮也需要重写注意实现鼠标移进、移出、按下的颜色改变(可以添加其他效果)。
    各个组件实现代码见GitHub。
  • 好友列表的实现

    要实现好友列表,第一个想到的就是树状结构。但是要实现相对漂亮的UI,利用树状结果不太好实现。

经过多次尝试,最终确定用JScrollPane来完成这个可能需要拖动的好友列表。但是利用JScrollPane后,不宜布局,若利用JPanel来制作好友,其大小不好固定。最终重写滚动条,具体实现见代码。