即时通讯软件的设计及实现.docx
《即时通讯软件的设计及实现.docx》由会员分享,可在线阅读,更多相关《即时通讯软件的设计及实现.docx(16页珍藏版)》请在冰点文库上搜索。
即时通讯软件的设计及实现
即时通讯软件的设计与实现
1系统实现模块
1.1服务端模块
服务端主要包括三个模块:
1.网络模块,建立TCP服务器,负责监听端口,与客户端建立连接并接受和发送数据。
2.应用模块,负责处理从网络模块接收到的数据,予以分析处理,进行转发或对数据库进行操作,并返回相关信息。
3.数据层,数据层用来与数据库建立连接,应用模块必须通过数据层来进行数据库的操作。
图4.1系统模块图
1.2客户端模块
用户端包块以下模块:
1.用户界面模块,此模块包括客户端的操作界面,由NetBeans编写的GUI,进行了较多的美化,采用偏向Vista的风格。
2.网络模块,此模块包括两个小模块,TCP模块用于和服务器端通讯,而UDP模块则负责和客户端进行P2P通讯。
3.聊天模块,此模块负责在用户聊天时候,对聊天的数据进行封装,对聊天的图片也进行压缩,以适用网络传输。
在接受到聊天数据之后,又会对聊天数据进行分解操作,最后生成聊天内容。
4.群组模块,此模块包括群组聊天、群组的创建、管理、更新等功能。
5.文件传输模块,此模块用于实现客户端之间的文件传输功能。
6.视频聊天模块,此模块用于实现客户端之间的视频聊天功能。
7.系统设置记录模块,此模块用于实现保存聊天记录,登录日志,以及保存系统设置信息的功能。
图4.2客户端模块图
2数据库设计
2.1用户表设计
用户表是系统中的基础表,主要用来记录用户注册时的各种信息,此表还有一个从表userlogin表用来记录用户登录和状态信息。
[12]
Userinfo表
字段名
字段类型
字段长度
备注
ID
Int
11
主键
Userid
Int
11
用户ID
Name
Varchar
20
用户名
Sex
Varchar
2
性别
age
Int
4
年龄
City
Varchar
50
城市
mail
Varchar
50
信箱
address
Varchar
60
地址
Telephone
Varchar
15
face
int
2
头像号
UserLogin表
字段名
字段类型
字段长度
备注
Userid
Int
11
用户ID
Pass
Varchar
20
用户密码
Fettle
Int
2
用户状态
Ipaddress
Varchar
20
IP地址
2.2用户关系表
用户关系表是用来保存用户之间的好友关系的数据表,查询好友列表的时候要从此表中查询与自己ID想关联的好友ID,type字段可以设置两者的关系,其中1为好友关系,0为黑。
字段名
字段类型
字段长度
备注
Id
int
11
主键
Userid
Int
11
用户ID号
Frendid
Int
11
好友ID号
Type
Int
4
关系类型
其他表,如组群表,族群信息表就不在此一一列出。
3系统模块的详细设计
此章节将叙述系统部分模块的设计方法和具体实现。
3.1网络模块的设计实现
本系统采用TCP和UDP混合的网络通讯,客户端与服务器之间登录验证时采用TCP连接,登录之后,客户端与服务器采用UDP方式保持通讯,客户端和客户端之间采用UDP连接,如果无法直接连接,通过服务器申请UDP穿透NAT,如果失败,则消息通过服务器中转传递。
因为要在网络通讯中直接传递对象,故采用基于JavaNIO的I/O的Cindy异步框架,Cindy是一个Java异步I/O框架,提供了一个统一高效的模型,同时支持TCP、UDP以及Pipe,并能够方便的在异步和同步操作之间进行切换。
目前其实现是基于JavaNIO,并计划通过JNI来支持各操作系统上本身提供的异步I/O功能,应用可以方便的通过运行期属性来方便的切换到更为高效的实现上。
[13]
JavaNIO包虽然提供了非阻塞I/O模型,但是直接使用NIO的非阻塞I/O需要成熟的网络编程经验,处理众多底层的网络异常,以及维护连接状态,判断连接超时等等。
对于关注于其业务逻辑的应用而言,这些复杂性都是不必要的。
不同Java版本的NIO实现也会有一些Bug,Cindy会巧妙的绕开这些已知的Bug并完成相应功能。
并且NIO本身也在不断发展中,Java1.4的NIO包中只实现了TCP/UDP单播/Pipe,Java5.0中引入的SSLEngine类使得基于非阻塞的流协议(TCP/Pipe)支持SSL/TLS成为可能。
使用Cindy,应用可以在同步和异步之间进行无缝切换,对于大部分操作是异步,可某些特殊操作需要同步的应用而言,这极大的提高了易用性。
[14]
1.服务器端编码:
SessionAcceptoracceptor;
acceptor=SessionFactory.createSessionAcceptor(SessionType.TCP);
acceptor.setListenPort(port);//设置监听端口port为端口号
acceptor.setAcceptorHandler(newSessionAcceptorHandlerAdapter());
//设置SessionAcceptorHandler;
SessionAcceptorHandler接口是处理SessionAcceptor产生的各种事件,
SessionHandler接口则用于处理Session产生的各种事件。
acceptor.start();//开始执行,服务器开始监听TCP连接。
SessionAcceptorHandlerAdapter类是处理acceptor时间的接口,里面包括了各种方法,我们主要使用到对象接收事件(objectReceived),我们接受到对象以后再对其进行处理。
代码如下:
newSessionAcceptorHandlerAdapter(){
publicvoidsessionAccepted(SessionAcceptoracceptor,Sessionsession)throwsException{
session.setPacketEncoder(newSerialEncoder());
session.setPacketDecoder(newServerPacketDecoder());
session.setSessionHandler(newSessionHandlerAdapter(){
publicvoidobjectReceived(Sessionsession,Objectobj){
/****这里可以得到接受到的对象,我们封装成Message类****/
Messagemessage=(Message)obj;
}
}
2.服务端编码
首先先生成一个TCP的Session,然后为session设置远程IP地址,最后通过设置SessionHandlerAdapter()来处理事件。
Sessionsession=SessionFactory.createSession(SessionType.TCP);
session.setRemoteAddress(newInetSocketAddress(address,port));
session.setPacketEncoder(newSerialEncoder());
session.setPacketDecoder(newSerialDecoder());
session.setSessionTimeout(0);
session.setSessionHandler(newSessionHandlerAdapter());
3.2用户登录的具体实现
前面介绍了Cindy的网络框架,现在我们来演示用户登录时候的流程。
用户登录请求处理模块是用户通过了验证(验证过程在信息查询服务器上完成),取得了合法登录密钥之后,登录文字通讯服务器的第一步。
用户登录请求处理模块的处理过程是:
接收用户登录请求,首先判断其登录密码是否正确(登录密码是用户注册时候输入的口令,服务器收到该请求后,首先查询数据库中此用户ID的密码,然后判断和用户发送的登录密码是否一致)。
如密钥不正确则关闭该连接,并发送密码错误的信息。
如果密钥正确,则修改该用户在数据库中的IP记录,并从数据库中读取出其所有的好友列表,并将此用户上线的消息发送给其好友,服务器同时将此用户此时此刻登录的情况记录在日志中。
客户端收到密码验证正确的消息之后,会受到服务器发送来的好友列表和详细信息,并通过这个好友列表构建好友信息,完成登录。
在客户端中,我们提供了隐身上线,和保存密码两个选择项,其内容保存在Hitayo.ini文件中。
在登录窗口中,可以手动输入服务器的IP地址,并将新的服务器地址保存进服务器选择列表,下次登录时,可以自动选择到上次登录的服务器地址,同时还具备检测服务器是否畅通的功能。
登录窗口界面如下:
图4.3客户端登录界面
3.3聊天模块的具体实现
一款即时通讯软件,最基本的功能莫过于即时聊天功能。
在本系统中,对聊天信息重新封装(ChatSeria类),使系统的聊天功能支持对文字的字体、大小、粗细、颜色进行设置,还能够在文字中插入图片,并能支持快捷键,用Ctrl+Enter可以发送消息。
图4.4聊天窗口界面
实现功能的关键在于对聊天信息封装和对文字中图片的定位和还原,以下代码实现了图文混合后对聊天信息的还原:
for(intx=0;x<5;x++){
end=chat.getMessage().indexOf('?
');
if(end==0){
//获得光标位置
jp.setCaretPosition(jp.getStyledDocument().getLength());
jp.insertIcon(newImageIcon(ImageIO.read(new
ByteArrayInputStream(chat.getImage()[x]))));//把图片插入光标位置
chat.setMessage(chat.getMessage().substring(++end));
}else{jp.getStyledDocument().insertString(jp.getStyledDocument().getLength(),chat.getMessage().substring(begin,end),null);//插入文字
chat.setMessage(chat.getMessage().substring(end));
x--;}
if(chat.getMessage().indexOf('?
')==-1){
jp.getStyledDocument().insertString(jp.getStyledDocument().getLength()
//插入文字
chat.getMessage().substring(--end),null);
break;
}
}
其原理是通过重写JTextPane的replaceSelection方法,改变了图标的位置标记,然后分别保存文本和图片以及字体属性,发送给接受端,接受端接受到信息以后,首先用以上算法,找到图片的标记,再把光标移动到那个位置,把图片组中的图片插入其中。
最多可以插入5X图片,但是经过测试如果图片过大,可能导致发送失败。
3.4视频模块的具体实现
视频聊天是即时聊天软件发展的一个新的高度,因为IM支持视频聊天是可以必不可少的功能。
JAVA实现对摄像头的控制需要通过JMF多媒体框架。
JMF实际上是Java的一个类包。
JMF2.1.1技术提供了先进的媒体处理能力,从而扩展了Java平台的功能。
这些功能包括:
媒体捕获、压缩、流转、回放,以及对各种主要媒体形式和编码的支持,如M-JPEG、H.263、MP3、RTP/RTSP(实时传送协议和实时流转协议)、MacromediasFlash、IBM的HotMedia和Beatniks的RichMediaFormat(RMF)等。
JMF2.1.1还支持广受欢迎的媒体类型,如Quicktime、MicrosoftsAVI和MPEG-1等。
[15]
本模块的设计思路是,通过JMF获得视频的数据流,然后将数据流还原成每一帧的图像,再将图像压缩后转换成字节流采用UDP包发送给接收方,接收方接受到信息后,从字节流中生成图像,再将图像绘制在JPanel上,我们重写JPanel的paintponent方法,并添加一个方法(UpVoide(Imageimg))不断刷新JPanel上的图片,这些都在独立的一个线程上操作,这样视频和聊天可以同时进行,经过测试,在网络较通常的情况下能获得比较好的效果。
图4.5视频聊天窗口界面
JMF获得视频图像的实现代码:
Stringstr="vfw:
MicrosoftWDMImageCapture(Win32):
0";
CaptureDeviceInfodi=CaptureDeviceManager.getDevice(str);
MediaLocatorml=di.getLocator();
dataSource=JMFUtils.createCaptureDataSource(null,null,
str,di.getFormats()[2]);
Playerplayer=Manager.createRealizedPlayer(dataSource);
player.start();
//获得opnent可以直接用来播放视频
p=player.getVisualponent();FrameGrabbingControlfgc=(FrameGrabbingControl)player.getControl(
"javax.media.control.FrameGrabbingControl");
Bufferbuf=fgc.grabFrame();
BufferToImagebufferToImage=newBufferToImage
((VideoFormat)buf.getFormat());
Imageimg=bufferToImage.createImage(buf);
Img为获得的视频图片,将此帧压缩成JPG后封装给接收方,接受方收到数据以后显示视频的代码如下:
protectedvoidpaintponent(Graphicsg){
super.paintponent(g);
if(img!
=null){
g.drawImage(img,0,0,this.getWidth(),this.getHeight(),this);
}
}
publicvoidUpVoide(Imageimg){
this.img=img;
this.repaint();
}
一个新的线程将每次接受到的Image对象作为参数调用UpVoide方法,则能快速刷新JPanel上的图像,达到视频播放的效果。
3.5P2P穿透NAT的实现步骤
NAT的穿透技术在P2P的的发展起到了至关重要的作用,可以说如果没有穿透NAT的方法,P2P绝不会成为一项如此热门的领域,因为P2P穿透NAT使大量的内网用户也能够享受到P2P带来的效率和速度了。
假设ClientA和ClientB,分别两个内网中的PC,其中ClientA的IP地址是192.168.0.20,其网关是202.187.45.3,ClientB的IP地址是192.168.0.10,其网关是187.34.1.56,服务器的IP地址是219.237.60.1,那么P2P具体的实现步骤如下:
1.ClientA登录服务器,NATA(ClientA的网关)这次的Session分配了一个端口60000,那么ServerS收到的ClientA的地址是202.187.45.3:
60000,这就是ClientA的外网地址了。
2.ClientB登录ServerS,NATB给此次Session分配的端口是40000,那么ServerS收到的B的地址是187.34.1.56:
40000
图4.6UDP穿透示意图
3.ClientA通过ServerS得到B的IP地址是187.34.1.56:
40000,然后ClientA向此IP发送信息,NATB抛弃此条信息。
4.ClientA通知ServerS,申请UDP穿透。
5.ServerS通知ClientB,并将ClientA的IP地址和端口告诉ClientB;
6.ClientB收到信息以后,像ClientA也发送一条消息,便会在NATB上留下一个Session。
7.ClientA再向ClientB发送信息时候,NATB会将消息转发给ClientB。
8.完成UDP穿透,ClientA和ClientB可以互相通讯。
3.6文件传输模块的设计
文件传输是即时聊天软件的主要功能之一,而文件的特殊性也决定了对于通过网络通信实现文件的传输,可靠性与完整性的要求都很高。
根据需求分析在本模块儿的设计中,我们未采纳UDP的传输方式。
但是,目前处于两个原因的考虑:
1)IP地址短缺;2)网络安全——防止黑客软件和病毒的泛滥。
大部分上网的个人计算机都被“隐藏”于防火墙或者NAT之后。
这就在很大程度上阻挡了面向连接的对等端实现TCP互联。
因此,在此模块中,为适应各种情况,保证文件的成功传输,我们采取了P2P和C/S模式并存,互为补充的模式,以P2P方式为主。
在进行文件传输时,首先采取P2P方式进行互联。
如果由于任何原因点对点方式不能成功,系统或自动采取点对服务器方式,从而确保文件传输的成功,模块设计结构如图
图4.7P2P连接示意图
系统采用TCP的方式发送,通过CINDY的框架下,对文件进行分包发送的代码如下:
发送方(部分)
for(inti=1;i<=QUEUE_SIZE;i++){
Bufferbuffer=BufferFactory.allocate(MESSAGE_SIZE);
buffer.position
(2);
intreadCount=buffer.read(fc);
//readCount为-1则文件已经读取完毕,发送完成关闭
if(readCount==-1){
buffer.release();future.getSession().close();
}else{
buffer.putUnsignedShort(0,readCount).flip();
//开始发送
session.send(buffer);
}
接收方(部分)
Bufferbuffer=(Buffer)obj;
if(fc==null){
fileName=buffer.getString(Charset.UTF8,buffer.remaining());
fc=newRandomAccessFile(fileName,"rw").getChannel();
}else{
while(buffer.hasRemaining())
buffer.write(fc);//写入文件
}
3.7组群功能的具体实现
组群功能是即时聊天软件的一大进步,取代了曾经的基于WEB的聊天室。
本系统的组群功能支持,组群的建立,加入组群,管理组群,组群公告,组群聊天等功能,每一个用户登录以后,都可以在组群栏中创建自己的组群,并添加用户到组群中来。
创建组群的用户将成为组群的管理员,能够管理组群用户和修改组群的资料信息等。
组群聊天模式采用TCP连接服务器转发,之所以采用这种模式是考虑到所以用户已经和服务器连接起来了,通过TCP转发能够降低程序的复杂性,不过从实际考虑,如果要投入实际运行,应该将TCP的组群聊天室改为UDP形式。
组群聊天同样支持文字的字体、颜色、大小、粗细进行设定,同时也支持图文并茂。
3.8聊天记录模块的实现
本系统支持聊天记录的保存和查阅,在这里采用的是文件保存的形式,将聊天记录保存为文本文件,位置在程序目录中的用户ID目录里,以好友的ID命名。
选择以文本文件作为聊天记录的载体是因为若使用JAVADB数据库来储存聊天记录,会降低程序运行速度,弊大于利。
if(newFile(Dir).exists());
//如果目录不存在则建立目录
newFile(Dir).mkdir();
if(chatnote.exists())
//如果文件不存在则建立文件
chatnote.createNewFile();
FileOutputStreamout=newFileOutputStream(chatnote,true);/
StringBuffersb=newStringBuffer();
//将用户名、时间和聊天记录加入StringBuffer
sb.append(friend.getName()+""+getDate()+"\r\n"+chat+"\r\n");
//先转换成“UTF-8”后写入文件
out.write(sb.toString().getBytes("utf-8"));
out.close();
若要查看聊天记录,可以直接到软件目录进入用户ID的文件夹直接查阅记录,或者在软件中进行查看记录,聊天记录查看界面如下:
图4.8聊天记录查看界面
由于系统比较庞大,不能讲解所有的具体实现步骤,所以只选择部分比较关键的模块进行叙述。
4小结
本文主要介绍了本人综合运用Swing、Cindy网络框架、JMF多媒体框架、MySQL数据库开发设计了一个P2P的即时聊天软件。
软件中的部分模块的设计思想和实现部分的关键代码。