您的位置:首页 > 其它

Internet 即时通信系统的设计与实现

2016-07-25 09:22 931 查看
系统设计与实现

开发平台及工具:Windows2000,Jbuilder4(J2SDK1.3),Together4.2。

客户端程序运行环境:拥有支持 Java 的浏览器的任何平台。

服务器程序运行环境:拥有 JVM 的任何平台。

1. 需求分析

图 2。1。1 为系统的 Use Case 图。

图 2。1。1

图 2。1。1


用例(Use Case): 远程会话

系统范围(Scope):服务器端系统

级别(Level):顶层(Summary)

外界系统描述(Context of Use):为了与该系统交互,外界系统为远程客户,而每一客户对应于一个客户端程序,客户通过客户端程序与系统交互

主要执行者(Primary Actor):客户

典型成功场景(Main Success Scenario):

客户通过客户端程序发出”申请新账号”的请求,并提供登录账号和登录密码等申请信息;

服务器端程序对该客户提交的信息进行验证,并发送”账号申请成功”信息;

客户接收”申请账号成功”信息后,继续发送请求;

服务器端程序接收该请求并进行相应处理,然后将执行结果返回给客户;

重复执行步骤 3 和步骤 4,直到客户发送”会话结束”信息。这时服务器程序完成结束前的处理工作后,断开与客户的连接;

扩展(Extensions):

1a.:系统发现该账号已经存在

1a.1.:系统返回”该账号已存在”信息,客户可以选择另选账号或者退出

1b:客户提交”登录”信息:

1b.1.:系统对客户身份进行验证:

1b.1a.:验证通过,返回”登录成功”信息

1b.1b:验证不能通过,返回”登录失败”信息,客户可以再尝试登录或者退出

说明:典型成功场景的第 1 步可以用 1a 代替,接下来是 1a.1;或者用 1b 代替,后接 1b.1, 再接 1b.1a 或者 1b.1b。

概要设计



该系统分为两大部份:客户端程序和服务器端程序。客户端程序采用 Java 小程序,通过 socket 与服务器端程序通信;服务器端程序采用 Java Application,同样采用 socket 与客户端程序进行交互。考虑到即时通信的准确性要求,通信协议采用 TCP。

详细设计

服务器端程序设计:

服务器端完成的功能是:对服务器的某一可用端口进行监听,以获得客户端请求,从而对客户端请求进行处理。因为是多客户同时请求,所以要采用多线程,为每一个在线用户分配一个线程,实时处理每个客户端的请求。因此,

对服务器端程序抽象如下:(图 2。3。1)

a .公共数据处理(Common Data Processing)

处理公共数据。如在线人数统计,客户的公共数据(如通知等),客户数据资料的存储与读取等(与数据库交互);

b . 端口监听器(Port Listener)

监听服务器某一端口,为每一在线客户建立一个会话线程;

客户请求处理(Client Request Processing)

处理客户的请求。根据客户的请求执行相应的操作。

服务器管理器



服务器端的管理工具,如对数据进行统计,紧急情况的处理等。

服务器端类的设计(图 2。3。2 和图 2。3。3):

公共数据处理类 CmDataProcessor(Common Data Processor):该类包含客户所共有的数据,以及如何对这些数据进行处理。

端口监听类 PortListener(Port Listener):该类实现了 java. lang. Runnable 接口,从服务器程序初始化完成后一直运行。由于目前 JDK 只支持同步通信,在没有客户请求时,该线程处于等待状态;一旦有客户请求到来,便继续执行。这时服务器程序可以通过 java. net. ServerSocket. accept() 方法获得客户端请求的 java. net. Socket 对象。然后用这个 Socket 对象为参数构造一个新的线程:ClientSession 的实例(类 ClientSession 以下将作介绍)。然后在 ClientSession 实例中用该 Socket 对象构造一个输出流 java. io. PrintStream 和一个输入流 java. io. BufferedReader,以后,每个客户就可以通过这一对输入输出流与服务器交互了。应该注意的是,ServerSocket 对象并不是在该对象内创建的,而是在服务器程序初始化时创建的。因为 socket 是进程间的通信,在线程中创建将会失败。客户端程序也是如此。

客户会话类 ClientSession(Client Session):该类继承自 java. lang. Thread 类,由 PortListener 创建。一般的,每一个在线客户都对应一个 ClientSession 的实例。该类用 parseRequest()方法解析客户发来的请求,进行相应处理。该线程在客户会话期间一直运行,通过 I/O 流读取和发送数据(I/O 流即从 PortListener 监听线程获得的 java. net. Socket 对象而创建的 java. io. PrintStream 和 java. io. BufferedReader 实例),直到客户退出才撤销。该类和类 ClientSession 一样都实现了 java. lang. Runnable 接口,故都有一个 run()方法。该方法的结束标志着该线程将结束。

服务器管理类 ServerManager(Server Manager):管理服务器。拥有管理权限的客户(管理员)可以远程操作服务器程序,包括运行、停止服务器,广播通知,给指定客户发送消息等特权操作。





客户端程序设计(图 2。3。4)

客户端完成的功能是:建立与服务器的连接;向服务器发送功能请求,接收来自服务器的信息,完成与主机或其他客户交互;断开与服务器的连接。客户端程序相对服务器端程序来说属于 LightWeight(轻量级)。这是由本系统的自身特点决定的。所以,对客户端程序抽象如下:

客户请求发送器:负责功能请求的发送。如登录请求等。

服务器信息接收器:负责接收来自服务器端的信息。如请求处理结果等。

客户端类的设计:

请求发送器(RequestSender):该类发送客户端的功能请求。客户通过客户端用户界面提交要执行的操作,然后由该类将客户提交的信息封装成服务器端程序可以理解的功能请求发送出去。

信息接收器(Receiver):该类接收来服务器端的信息。这些信息可以是客户请求的处理结果,也可以是服务器端的广播通知。为保证实时性,该类实现了 java. lang. Runnable 接口。在客户会话期间,该类将一直运行,实 时的将来自服务器端的信息反馈给客户。该类接收信息后,应该对该信息 做相应处理。如通知客户已登录成功等。这些操作都将在 run()方法中 实现。



4. 实现

以上的系统设计是一个即时通信系统的总体框架,根据实际情况,可以添加或者修改。下文就以“远程会议系统“为例来实例化这样一个通信系统。

我们知道,远程会议系统有几个方面的特点:实时交互;准确传输信息;多客户等。所以,完全可以用该系统框架来实现 ( 这里只给出核心代码 )。

首先我们来实现服务器端程序。为了便于对服务器程序的管理,服务器端程序采用了 GUI 界面。在该程序初始化时应该实现对可用端口的监听(程序清单 1。1)。

程序清单 1。1。1

try {

socket = new ServerSocket(NetUtil.SLISTENPORT);

}

catch (Exception se)

{

statusBar.setText(se.getMessage());

}

其中,NetUtil.SLISTENPORT 是服务器的一个可用端口,可以根据实际情况确定。NetUtil 是一个接口,其中包含了该系统用到的各类常数。NetUtil.SLISTENPORT 就是 NetUtil 中的一个整型常数。如果端口监听抛出异常,GUI 中的 statusBar 将给出提示。监听成功后可以启动监听线程了(程序清单 1。1。2)。

程序清单 1。1。2

listenThread=new Thread (new ListenThread ());

listenThread.start();

以上程序中 listenThread 是一个 Thread 实例,用来操作监听线程。监听线程实现如下(程序清单 1。2。1):

程序清单 1。2。1

public class PortListener implements Runnable{

ServerSocket socket; // 服务器监听端口

FrmServer frm; //FrmServer 为 GUI 窗口类

ClientSession cs; // 客户端会话线程

PortListener (FrmServer frm,ServerSocket socket) {

this.frm=frm;

this.socket=socket;

}

public void run () {

int count = 0;

if (socket == null) {

frm.statusBar.setText(“socket failed!”);

return;

}

while (true) {

try {

Socket clientSocket;

clientSocket = socket.accept(); // 取得客户请求 Socket

// 用取得的 Socket 构造输入输出流

PrintStream os = new PrintStream(new

BufferedOutputStream(clientSocket.getOutputStream(),

1024), false);

BufferedReader is = new BufferedReader(new

InputStreamReader(clientSocket.getInputStream()));

// 创建客户会话线程

cs = new ClientSession(this.frm);

cs.is = is;

cs.os = os;

cs.clientSock = clientSocket;

cs.start();

} catch (Exception e_socket) {

frm.statusBar.setText(e_socket.getMessage());

}

}

}

}

监听线程一直在后台运行。当有客户请求到来时,监听线程创建与该客户进行会话的 ClientSession 实例。这时,监听线程会等待另外客户请求的到来,然后又创建会话线程,如此循环下去。客户会话则通过会话线程进行。客户会话线程主要的工作就是怎样处理客户请求。ClientSession. parseRequest () 就是处理客户请求的。这个方法的内容应该根据实际应用的需要来确定。这里只实现了一些很简单的功能。如会议大厅发言,私下交谈等。ClientSession 的实现代码见程序清单 1。3。1。

程序清单 1。3。1

public class ClientSession extends Thread {

FrmServer frm; //GUI 窗口类

BufferedReader is = null; // 输入流

PrintStream os = null; // 输出流

Socket clientSock = null; // 客户请求 Socket

int curContent = 0;

public ClientSession (FrmServer frm) {

this.frm=frm;

}

public void run () {

parseRequest(); // 处理客户请求

}

public void destroy () {

try {

clientSock.close();

is.close();

os.close();

} catch (Exception cs_e) {}

}

// 客户请求处理方法,只实现了大厅发言及私下交谈

private void parseRequest () {

String strTalking;

String strUserID;

String strMain = “”;

String strPerson = NetUtil.PERSONINFO + “\n”;

boolean flagEndProc = false;

while (true) {

try {

while ((strTalking = new String(is.readLine())) != null) {

if(strTalking.equals(NetUtil.CHATSTART)){ // 客户会话开始

strUserID = new String(is.readLine());

// 将所有谈话内容发送给刚登录的客户

for (int i = 0; i < frm.dataProcessor.vecMainTalk.size(); i++) {

strMain += (String)frm.dataProcessor.vecMainTalk.get(i);

strMain += “\n”;

}

curContent = frm.dataProcessor.vecMainTalk.size();

os.println(strMain);

os.flush();

for (int j = 0; j < frm.dataProcessor.vecP2PInfo.size(); j++) {

strPerson += (String)frm.dataProcessor.vecP2PInfo.get(j);

strPerson += “\n”;

}

os.println(strPerson);

os.flush(); // 将所有在线用户名单发给新加入的客户

os.println(NetUtil.DIVIDEDLINE);

os.flush();

while (true) {

this.sleep(1000);

String strContent = “”;

// 如果有人发言,则把发言发给在线客户

if (frm.dataProcessor.vecMainTalk.size() > curContent) {

for (int ci = curContent; ci
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: