[置顶] Chat Room:基于JAVA Socket的聊天室设计
d0304 更新功能实现
d0312 更新部分图片&UI设计部分
d0318 更新功能实现
d1222 实现添加好友功能、实现注册功能、修改大量BUG
github:https://github.com/He11oLiu/ChatRoom.git
==========================================================
即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息、档案、语音与视频交流。如QQ,微信都属于即时通讯。
本篇博客将详细记录我在JAVA上搭建一个自己的即时通讯工具的实现方法,如有错误的地方或者建议,请多多提出。
功能实现
基本技术
Socket 与 ServerSocket
作为一个即时通讯工具,客户端(Client)和服务器(Server)是两个必不可少的部分。首先我们就来解决服务器和客户端连接的问题。
Socket的英文原义是“孔”或“插座”。
Socket用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。建立了一个Socket后,从这个Socket中读取I/O流,就可以实现两个程序的通讯。
而服务器需要与多个客户端进行双向的通讯连接,并非单一的连接。于是可以通过每接收到一个Client后就开一个单独的进程,来完成与该Client的通讯步骤。这样就可以同时接受多个Client,从而实现服务器的功能。
客户端建立:
Java提供了Socket类,用来建立客户端
提供的构造方法如下:
当调用构造方法,构造一个新的Socket的时候,就是向指定的端口请求连接。
通过以下方法,可以获取该Socket的I/O流
通过close方法,关闭该Socket
服务器建立:
Java提供了ServerSocket类,用来建立服务器。
提供的构造方法如下:
可以通过Telnet来测试是否成功的建立了服务器。(win7后默认关闭,在控制面板-程序-启用或关闭windows功能下开启)telnet localhost <port>
或者
telnet 127.0.0.1 <port>
该类下自带了
accept方法用来接收连接,返回一个Socket对象。接收该对象,并调用刚才提到的getInputStream()方法以及getOutputStream()方法,即可实现获取该Socket的I/O流。从该流上读取信息或者写入信息,即可达到通讯的目的。要注意的是,此时客户端的Input流对应着服务器端的Output流。
同样ServerSocket也带了close方法,用来关闭服务器。
通讯协议设计
通讯协议具体内容
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 | 可聊天的内容 |
发送一次信息的总流程
框架设计
界面层
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数据库,数据库结构如下。
数据访问层提供UserModel,其中包括
- 根据JK号获取用户内容
- 验证用户信息
- 获取用户好友列表
- 增加好友,删除好友
UI设计
Metro UI
Metro UI是基于瑞士平面设计原则,其最初在Windows XP的Windows Media Center就中有体现,这有利于以文字为主的界面导航。2006年著名的Zune播放器开始使用类似Metro的设计风格。微软的设计师计划重新设计现有用户界面、更清爽的排版和较少的重点以便于用户使用。Zune的桌面客户端程序也使用了不同于以往Portable Media Center用户界面,其清爽排版和设计给用户耳目一新的冲击。
Metro UI的特点:强调信息本身
参考资料:http://www.csdn.net/article/2012-02-01/310896/1
窗体设置无边框以及拖动选项
为了彻底改变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来制作好友,其大小不好固定。最终重写滚动条,具体实现见代码。
[置顶] Chat Room:基于JAVA Socket的聊天室设计的更多相关文章
- 基于Java的在线聊天室
概述 Java socket编程,实现一个在线聊天室, 实现在线用户群聊,私聊,发送文件等功能. 详细 代码下载:http://www.demodashi.com/demo/13623.html 一. ...
- Java Socket 多线程聊天室
本来这次作业我是想搞个图形界面的,然而现实情况是我把题意理解错了,于是乎失去了最初的兴致,还是把程序变成了功能正确但是“UI”不友好的console了,但是不管怎么样,前期的图形界面的开发还是很有收获 ...
- 移动开发首页业界资讯移动应用平台技术专题 输入您要搜索的内容 基于Java Socket的自定义协议,实现Android与服务器的长连接(二)
在阅读本文前需要对socket以及自定义协议有一个基本的了解,可以先查看上一篇文章<基于Java Socket的自定义协议,实现Android与服务器的长连接(一)>学习相关的基础知识点. ...
- workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)
workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...
- Java Socket 网络编程心跳设计概念
Java Socket 网络编程心跳设计概念 1.一般是用来判断对方(设备,进程或其它网元)是否正常动行,一 般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉.用于 ...
- java Socket多线程聊天程序
参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...
- 基于springboot的websocket聊天室
WebSocket入门 1.概述 1.1 Http #http简介 HTTP是一个应用层协议,无状态的,端口号为80.主要的版本有1.0/1.1/2.0. #http1.0/1.1/2.0 1.HTT ...
- Socket.IO聊天室~简单实用
小编心语:大家过完圣诞准备迎元旦吧~小编在这里预祝大家元旦快乐!!这一次要分享的东西小编也不是很懂啊,总之小编把它拿出来是觉地比较稀奇,而且程序也没有那么难,是一个比较简单的程序,大家可以多多试试~ ...
- 分享基于 websocket 网页端聊天室
博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...
随机推荐
- 有關於USB保固
================================================= 版權聲明:如需轉載,請列明出處:HingAglaiaWong@博客園 支持原創,是對作者最好的的鼓勵 ...
- 基于.NET CORE微服务框架 -Api网关服务管理
1.前言 经过10多天的努力,surging 网关已经有了大致的雏形,后面还会持续更新完善,请大家持续关注研发的动态 最近也更新了surging新的版本 更新内容: 1. 扩展Zookeeper封装2 ...
- 【Java学习笔记之八】java二维数组及其多维数组的内存应用拓展延伸
多维数组声明 数据类型[][] 数组名称; 数据类型[] 数组名称[]; 数据类型数组名称[][]; 以上三种语法在声明二维数组时的功能是等价的.同理,声明三维数组时需要三对中括号,中括号的位置可以在 ...
- promise处理多个相互依赖的异步请求
在项目中,经常会遇到多个相互依赖的异步请求.如有a,b,c三个ajax请求,b需要依赖a返回的数据,c又需要a和b请求返回的数据.如果采用请求嵌套请求的方式自然是不可取的.导致代码难以维护,如何请求很 ...
- 【Spring】浅谈ContextLoaderListener及其上下文与DispatcherServlet的区别
一般在使用SpingMVC开发的项目中,一般都会在web.xml文件中配置ContextLoaderListener监听器,如下: <listener> <listener-clas ...
- opencv 访问图像像素的三种方式
访问图像中的像素 访问图像像素有三种可行的方法方法一:指针访问指针访问访问的速度最快,Mat类可以通过ptr函数得到图像任意一行的首地址,同时,Mat类的一些属性也可以用到公有属性 rows和cols ...
- 开源社交系统ThinkSNS+ 0.7.3研发周报
什么是ThinkSNS+ ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+. 亲爱的粉丝,授权客户, ...
- QLineEdit IP地址校验
QLineEdit IP地址校验 原文出处:[上善若静水] 1.通过自定义类方式实现IP4地址范围限制输入: //--------------------TLineEditIP.h---------- ...
- JavaWeb(二)jsp运行原理,九大内置对象
JSP运行原理: 每个JSP页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理.JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ...
- Go语言中函数的实现
Go 语言函数 函数是基本的代码块,用于执行一个任务. Go 语言最少有个 main() 函数. 你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务. 函数声明告诉了编译器函数的名称,返回 ...