分享
 
 
 

用JAVA编制Internet电子白板软件

王朝厨房·作者佚名  2007-01-04
窄屏简体版  字體: |||超大  

前言

随着Internet的迅速发展,在Email、WWW、FTP等传统的非实时应用日趋成熟的同时,广大网络用户对在线实时交流的需求不断扩大,如网上会议、远程教学、协同工作等。这方面的应用软件也日益丰富起来。该类软件主要分为两类,一种是以目前BBS和主页上的聊天室为代表的纯文字型的交流工具;另一种就是本文要介绍的电子白板类交流工具。电子白板除了具备聊天室的全部功能外,更重要的是,它还引入了绘画图形交流功能,使网上交流的形象性和直观性大大加强了,弥补了文字交流的不足。当分布在Internet不同位置的用户用白板进行交流时,一个人在自己的白板上绘制的图形可以马上在别人的白板上显示出来,好象大家都在同一块白板上绘画,彼此间的距离感大大缩短了。

目前具有电子白板功能的软件有微软的Netmeeting等。此类软件在使用前需要用安装盘安装(Netmeeting是Internet Explorer 4.0的选装件),在设置完成后才能使用。相比而言,在主页中用Java Applet实现白板功能就显得优势很大。因为它不需要传统软件的下载、安装和设置的繁琐步骤,只要用支持JAVA的浏览器连接到该Applet所在主页,就可以使用白板。用于浏览器的普及性(大部分常用浏览器都支持JAVA,如InternetExploer3.0与Netscape Navigator 3.0及以上版本)使得这种电子白板的潜在客户群是巨大的。此外,用JAVA编制电子白板软件还有一个显而易见的优势,就是整个软件(尤其是白板服务器)无须重新修改编译就可在NT、Unix、Linux等支持JAVA的平台上运行。

目前基于浏览器和JAVA的电子白板正处于起步阶段,笔者尚未在网上看到该类软件。由于工作需要,笔者自行开发了一套此类电子白板软件。这里想将开发中的一些经验介绍给大家,以达到共同交流的目的。

工作原理

电子白板有两种实现模型,一种是无白板服务器,因此仅支持两个用户直接连结;另一种是有白板服务器,原则上不限制同时上线人数和交谈室个数,具体实现上可视服务器性能和需要而定。本文要介绍属于后者。

当用户需用白板与他人交流时,需要先用浏览器连到Applet所在主页,Applet运行后会连接到该白板服务器,和服务器建立TCP连接。每个用户可以在自己的白板上(嵌在Applet画面中)绘制图形和输入文字,Applet会将这些信息通过已建立的网络连接发往白板服务器,并不断侦听、接收来自白板服务器的图形和文字信息,将其再现在用户的白板中。白板服务器的作用是不断侦听、接收来自各Applet的信息,并将其转发给其他用户。

由于浏览器对Java Applet的限制,使得Applet只能访问发送该Applet的宿主主机,因此只能在该Applet所在主机上运行白板服务器,使得Applet能建立和远程白板服务器的联系。

功能设计

一个实用的电子白板系统应该具备以下基本功能:

用户在浏览到白板主页时,需登录后才能进行交流。这将提供交流时用的名字,必要时还可做权限检查。

用户能够根据交谈室的交谈主题选择参加和退出现有的交谈室,并且能建立新的交谈室。可以根据该电子白板系统的应用领域和需要附加一些权限设置。

用户只能和在同一交谈室中的用户交流。一个交谈室的信息对于别的交谈室是不可见的。

白板应至少具有一些如更换画笔颜色、清除画板(仅影响自己的白板)等方便用户的功能。

白板应具有一个操作提示和操作信息反馈栏,对用户显示一些操作提示和操作结果信息,这样可以方便用户使用。

用户应能暂停和恢复自己的白板工作。

为了增加白板的实用价值,可以考虑增加如与windows画笔工具类似的画正方形、圆形等类似功能。本例作为电子白板基本模型的建立,故没有加入这些功能。实际上,只要了解了下面要介绍的白板通讯协议集的设计原则,增加以上功能是非常容易的。

通讯协议集的制定

由于JAVA内置的标准基础通讯协议是TCP/IP,所以我们只需在其基础上建立电子白板的应用层协议集。协议集的模型将采用服务器/客户机的请求/应答模式。可以根据需要实现的白板功能制定通讯协议集,协议集中包括登录、图形传输(分服务器发出和客户机发出两部分)、文字传输(分服务器发出和客户机发出两部分)、服务器要求刷新交谈室及成员名单、客户机要求刷新交谈室及成员名单、加入指定交谈室、退出交谈室、建立新交谈室、暂停/恢复交流等部分。

由于已经依靠TCP/IP协议保证数据传输的正确性,所以在这个电子白板的通讯协议集的设计中应在保证功能的前提下尽量简洁,来提高带宽利用率。白板通讯协议集的细节可参考如下:

注意:

(S)表示该部分由服务器发出,客户机接收;

(C)表示该部分由客户机发出,服务器接收;

数据格式表示:引号之间表示字符串,(short)表示短整形数(2字节),(int)表示标准整形数(4字节)。

登录(C):"log"→用户名字符串。

图形传输(客户机发出)(C):"draw"→(int)颜色值→(short)直线起点横坐标→(short)直线起点纵坐标→(short)直线终点横坐标→(short)直线终点纵坐标。

图形传输(服务器发出)(S):"draw"→(int)颜色值→(short)直线起点横坐标→(short)直线起点纵坐标→(short)直线终点横坐标→(short)直线终点纵坐标。

文字传输(客户机发出)(C):"text"→用户在白板对话框中输入的文字字符串。

文字传输(服务器发出)(S):"text"→文字输入者姓名字符串→该用户输入的文字字符串。

服务器要求刷新交谈室及成员名单(S):"refresh"→交谈室1主题字符串→交谈室1中的用户1姓名字符串→交谈室1中的用户2姓名字符串→...→"complete"→交谈室2主题字符串→交谈室2中的用户1姓名字符串→交谈室2中的用户2姓名字符串→...→"complete"→...→最后一个交谈室主题字符串→最后一个交谈室中的用户1姓名字符串→最后一个交谈室中的用户2姓名字符串→...→"complete"→"ok"。

客户机要求刷新交谈室及成员名单(C):"refresh"。(服务器收到此命令,会执行前面的服务器要求刷新交谈室及成员名单子协议,以响应客户机请求)

加入指定交谈室(C):"join"→申请加入的交谈室主题字符串。

退出交谈室(C):"quit"。

建立新交谈室(C):"new"→申请建立的新交谈室主题字符串。(服务器收到此命令,会自动在该用户原来所在的交谈室中注销,并使该用户成为新交谈室的一员)

暂停交流(C):"pause"。

恢复交流(C):"continue"。

以上为本电子白板软件所遵循的通讯协议集。这个协议集的可扩充性很强,可以随时按增加的功能扩充协议集。例如需要传送圆形图案,则可将如下协议加到协议集中:"circle"→(int)颜色值→(short)圆心横坐标→(short)圆心纵坐标→(short)圆半径。

编程实现

程序设计分服务器JAVA Application和客户端Java Applet两部分进行。

编程中需要注意以下几点:

服务器程序不要采用客户端接入时建立用户线程,退出交谈室时销毁线程的工作流程。因为有些操作系统的线程操作机制不够健全,在线程销毁时线程所占资源不能被完全释放,以致于在白板服务器运行的过程中将逐渐消耗掉系统资源。所以应在服务器初始化时按照最大允许同时上线的用户数建立所有用户服务线程。这些线程将等待客户端接入,当用户退出交谈室时线程并不销毁,而是清除用户数据,重新进入等待接入状态,准备为下一个用户服务。这样就保证了白板服务器可以长期可靠运行。

当用户在白板上连续绘画时可能产生大量的图形数据,客户端Applet若在白板的AWT事件处理程序中完成将这些数据传输给服务器的任务,则很有可能由于网络I/O的瓶颈作用,使得AWT事件处理线程受阻,从而影响白板Applet、浏览器、其他正在运行的应用软件的界面相应性。解决的办法就是使Applet再建立一个后台绘图数据传输线程,白板的AWT事件处理程序将用户的绘图数据通过管道流(PipedStream)传输给这个线程后就返回,把网络传输的任务留给这个线程进行。

在服务器和客户端Applet中,协议集每一个子协议的实现都要分别建立一个同步块(synchronize),该子协议的全部操作都要在这个同步块内完成,以限制自由访问网络接插建立的输入流和输出流。使得当一个线程执行一个子协议时能够独占这些网络资源,而使别的线程不能访问这些资源,以保证线程能够完整正确地执行子协议。但是由于同步操作会降低线程调度和执行效率,所以要在确保子协议完整执行的前提下尽量缩小同步代码块的范围。

由于协议集中的数据类型既有字符串又有整形数,因此在程序中选用DataInputStream和DataOutputStream作为数据输入流和输出流。此外,由于UTF格式的文本支持中文字符集,且在字符串中已包含长度信息,可以方便数据读取,故在本程序的网络通信中的字符串全部采用UTF格式。设计自己的白板程序时,可以根据需要换用其他文本格式和编码规则,但一定要保证输入流和输出流采用的是同一种格式和编码。

考虑到目前Netscape Navigator 3.0和Internet Explorer 3.0目前仍普遍使用,而这两种浏览器的JAVA虚拟机都不支持较新的JDK1.1标准,所以在本程序的客户机Applet部分中没有使用JDK1.1特有的类库。

首先介绍服务器程序的编制。需确定主要的几个类及其成员函数。列表如下:

ChatServer类:服务器程序的启动类。

ChatServer():建立服务器管理界面。

initServer():建立服务器插结,初始化交谈室数据,按照允许同时上线的最大用户数建立多个用户服务线程。

go():启动用户服务线程。

quit():中止用户服务线程,关闭服务器插结,并释放全部资源。

User类:为Thread类的子类,实现用户服务线程,处理服务器/客户机会话。

run():等待客户端接入;客户端接入后初始化网络资源,进入循环侦听客户端请求的状态,并根据客户端的请求调用相应的协议处理程序。

sendText():按照文字传输子协议,接收用户发出的文字信息,并转发给同一交谈室的其他用户。

sendDraw():按照图形传输子协议,接收用户发出的图形信息,并转发给同一交谈室的其他用户。

commandCenter():为服务器接收到的指令选择运行适当的处理程序。

server_ask_refresh():服务器主动刷新交谈室及成员名单。

server_answer_reresh():响应客户机要求刷新交谈室及成员名单。

server_answer_log():响应客户机登录。

server_answer_join():响应用户加入指定交谈室请求。

server_answer_pause():响应用户暂停交流请求。

server_answer_continue():响应用户恢复交流请求。

server_answer_newRoom():响应用户建立新交谈室请求。

server_answer_quit():响应用户退出交谈室请求。

server_ask_text():服务器向客户机转发文字信息。

server_ask_draw():服务器向客户机转发图形信息。

refreshToAll():服务器刷新所有在线客户端的交谈室数据。

refreshToAllNotMe():服务器刷新除该用户外,所有在线客户端的交谈室数据。

socketClose():用户申请退出时调用,关闭网络插结,释放线程占用的网络资源。

RoomGroup类:保存所有交谈室数据,直接处理与交谈室有关操作。

newRoom():建立新交谈室。

join():加入指定交谈室。

quit():退出指定交谈室。

getUsersName():返回指定交谈室内的用户名。

getRoomID():按照交谈室主题查询并返回交谈室ID号。

Room类:保存并处理本交谈室用户数据。

join():加入本交谈室。

quit():退出本交谈室。

getUsersName():返回本交谈室的用户名。

DataBag类:包装图形和文字数据。

AboutDialog类:生成About对话框,可显示版权信息。

QuitDialog类:生成QuitDialog对话框,用于确认是否结束服务器运行。

客户机Applet包含的类及其主要成员函数如下:

ChatApplet类:为Applet类的子类,实现Runnable接口。是白板Applet的核心类,完成与用户交流和与服务器通讯的任务。

init():初始化Applet用户界面。

run()::循环侦听来自白板服务器的信息,调用commandCenter命令处理程序。

start():生成并启动Applet线程。

stop():中止Applet线程。

destroy():中止后台图形数据传送线程,调用关闭网络资源函数。

socketClose():关闭所有打开的网络插结和通讯流。

openSocket():建立网络插结和通讯流。

commandCenter():集中处理来自用户和服务器以及来自Applet内部的命令,调用相应的处理程序。

client_ask_refresh():客户机请求刷新交谈室数据。

client_answer_refresh():客户机接收服务器发送的交谈室数据。

client_ask_log():申请登录到服务器。

client_ask_join():申请加入指定交谈室。

client_ask_pause():暂停交流。

client_ask_continue():恢复交流。

client_ask_newRoom():申请建立新交谈室。

client_ask_quit():退出交谈室。

client_ask_text():发送用户输入的文字信息。

client_answer_text():接收服务器转发的其他用户的文字信息。

client_answer_draw():接收服务器转发的其他用户的图形信息。

refreshRoomList():显示交谈室列表。

refreshUserList():显示指定交谈室中的用户列表。

printChat_Area():将该用户和来自服务器的其他用户的输入的文字用指定格式显示在Applet的交谈文本框中。

sendText():判断用户输入的文字是否为有效字符串,若有效则调用发送文字函数。

drawBoard_Canvas():按DataBag对象的值,在Applet的白板上绘图。

boardCanvas_MouseDrag():当鼠标在白板上拖动时调用此函数,将鼠标轨迹包装成数据包发送给后台网络传输线程。

logButton_Clicked():当用户单击Applet的登录钮时调用此函数,建立网络插结和后台网络传输线程。

其他一系列用户界面事件处理函数:与用户交流,接收用户输入,直接处理或调用commanCenter函数执行用户命令。

RoomData类:存储该交谈室的主题和用户名的数据类

addUser():在该交谈室中添加用户名。

removeUser():在该交谈室中删除用户名。

getUsersName():返回所有在该交谈室中的用户名。

Rooms类:为Vector类的子类。存储所有交谈室的用户数据。

getRoomByName():按交谈室主题返回指定交谈室的RoomData对象。

addElement():添加RoomData对象。

SendDrawData类:为Thread类的子类,实现后台网络传输线程。

run():循环侦听来自Applet主线程的图形信息,并将其通过网络接插生成的输出流发送给服务器。

quit():关闭该线程与Applet主线程的连接流。

Logo类:为Canvas类的子类。用于在Applet中显示图标或版权信息。

此外程序中还定义了几个异常类,可以方便程序的异常处理机制设计,增加程序的可维护性和可读性。Applet徽标的默认图形文件名是"cdownlogo.jpg"(457×60),服务器像标的默认图形文件名是"icon.gif"(19×17)。服务器默认占用的端口号为10000。以上各项可以视需要修改。

结论

本文结合笔者的编程经验初步介绍了基于浏览器和JAVA的电子白板软件的程序设计过程,希望这些内容能够对广大软件开发人员有所帮助。欢迎大家与笔者联系(lfl@cenpok.net),就电子白板的一些问题进行切磋和交流。

附录内容:

本电子白板系统的完整源程序。已测试通过,可以直接编译运行。

嵌入Applet的主页示范。请注意主页中Applet的参数设置。

本电子白板客户端Applet的用户界面及操作方法简介。

附录1:源程序:

import java.net.*;

import java.io.*;

import java.awt.*;

import java.util.*;

import java.applet.*;

// class 1:server main class,服务器启动类

public class ChatServer extends Frame {

final static int DEFAULT_USERCOUNT=30;

final static int DEFAULT_PORT=10000;

final static String ICON_FILE="icon.gif";

int num_Line=0;

User[] users=null;

ServerSocket ss=null;

RoomGroup roomGroup=null;

int port=DEFAULT_PORT;

Image icon=null;

java.awt.TextArea message_TextArea;

java.awt.List users_List;

java.awt.Choice room_Choice;

java.awt.Label message_Label;

java.awt.Label room_Label;

java.awt.Label userList_Label;

java.awt.Button clean_Button;

java.awt.MenuBar mainMenuBar;

java.awt.Menu menu1;

java.awt.Menu menu3;

void initServer(int port) throws IOException {

users=new User[ChatServer.DEFAULT_USERCOUNT];

roomGroup=new RoomGroup(ChatServer.DEFAULT_USERCOUNT,room_Choice,

this);

try{

ss=new ServerSocket(port,5);

message_TextArea.appendText("\nserver socket init successed");

}catch(Exception e){

System.out.println(e);

message_TextArea.appendText("\nserver socket init error"+e);

}

for(int i=0;i< users.length;i++) {

users[i]=new User(this,i);

}

roomGroup.setUsers(users);

show();

go();

message_TextArea.appendText("\nserver started");

}

ChatServer(int port) throws IOException {

icon=getToolkit().getImage(ICON_FILE);

MediaTracker tracker = new MediaTracker(this);

tracker.addImage(icon,0);

try{

tracker.waitForAll();

}catch(Exception e){

System.out.println(e);

}

//{{INIT_CONTROLS

setLayout(null);

addNotify();

resize(insets().left + insets().right + 544,

insets().top + insets().bottom + 355);

//$$ openFileDialog1.move(36,276);

setTitle("Chat & WhiteBoard Server 1.0");

setIconImage(icon);

//}}

message_TextArea = new java.awt.TextArea();

message_TextArea.reshape(insets().left + 12,

insets().top + 36,396,240);

add(message_TextArea);

users_List = new java.awt.List(5,false);

add(users_List);

users_List.reshape(insets().left + 432,

insets().top + 144,76,110);

room_Choice = new java.awt.Choice();

add(room_Choice);

room_Choice.reshape(insets().left + 420,

insets().top + 60,100,1);

message_Label = new java.awt.Label("message");

message_Label.reshape(insets().left + 36,

insets().top + 12,100,24);

add(message_Label);

room_Label = new java.awt.Label("Chat room");

room_Label.reshape(insets().left + 420,

insets().top + 14,100,24);

add(room_Label);

userList_Label = new java.awt.Label("user List");

userList_Label.reshape(insets().left + 420,

insets().top + 108,100,28);

add(userList_Label);

clean_Button = new java.awt.Button("clean");

clean_Button.reshape(insets().left + 180,

insets().top + 12,60,24);

add(clean_Button);

//{{INIT_MENUS

mainMenuBar = new java.awt.MenuBar();

menu1 = new java.awt.Menu("File");

menu1.add("Restart");

menu1.add("Exit");

mainMenuBar.add(menu1);

menu3 = new java.awt.Menu("Help");

mainMenuBar.setHelpMenu(menu3);

menu3.add("About");

mainMenuBar.add(menu3);

setMenuBar(mainMenuBar);

//$$ mainMenuBar.move(4,277);

this.port=port;

initServer(port);

}// constructor 1 ended

ChatServer() throws IOException{

this(ChatServer.DEFAULT_PORT);

}// contructor 2 ended

void cleanButton_Clicked(Event event) {

//{{CONNECTION

// Hide the Frame

message_TextArea.setText("");

//}}

}

void About_Action(Event event) {

//{{CONNECTION

// Action from About Create and show as modal

(new AboutDialog(this, true)).show();

//}}

}

void Exit_Action(Event event) {

//{{CONNECTION

// Action from Exit Create and show as modal

(new QuitDialog(this, true)).show();

//}}

}

void Restart_Action(Event e) {

try{

quit();

initServer(port);

}catch(Exception e1){

message_TextArea.appendText("\ninit server error");

}

}

public synchronized void show() {

move(50, 50);

super.show();

}

void roomChoice_Action(Event e) {

String roomName=null;

int roomID=0;

users_List.clear();

roomName=room_Choice.getSelectedItem();

roomID=roomGroup.getRoomID(roomName);

for(int i=0;i< users.length;i++) {

if ((users[i].logged)&&(users[i].belongRoom)

&&(users[i].roomID==roomID)) {

users_List.addItem(users[i].name);

}

}

}

public boolean handleEvent(Event event) {

if (event.id == Event.WINDOW_DESTROY) {

quit();

hide(); // hide the Frame

dispose(); // free the system resources

System.exit(0); // close the application

return true;

}

if (event.target == room_Choice && event.id ==

Event.ACTION_EVENT) {

roomChoice_Action(event);

return true;

}

if (event.target == clean_Button && event.id

== Event.ACTION_EVENT) {

cleanButton_Clicked(event);

return true;

}

return super.handleEvent(event);

}

public boolean action(Event event, Object arg) {

if (event.target instanceof MenuItem) {

String label = (String) arg;

if (label.equalsIgnoreCase("About")) {

About_Action(event);

return true;

} else

if (label.equalsIgnoreCase("Exit")) {

Exit_Action(event);

return true;

} else

if (label.equalsIgnoreCase("Restart")) {

Restart_Action(event);

return true;

}

}

return super.action(event, arg);

}

public static void main(String args[]) {

try{

ChatServer chatServer=new ChatServer();

}catch(Exception e){

System.out.println(e);

}

}

public void go() {

for(int i=0;i< users.length;i++) {

users[i].start();

}

}// method ’go’ ended

public void quit() {

for(int i=0;i< users.length;i++) {

if (users[i]!=null) users[i].destroy();

}

try{

ss.close();

ss=null;

room_Choice.removeAll();

users_List.clear();

message_TextArea.appendText("\nserver socket closed");

}catch(IOException e){

System.out.println(e);

}

}// method ’quit’ ended

} // class ’ChatServer’ ended

//class 2:RoomGroup,服务器用的交谈室数据类

class RoomGroup {

Room[] rooms=null;

final static String DEFAULT_ROOM_SUBJECT="默认";

final static int DEFAULT_ROOM_ID=0;

Choice room_Choice=null;

List users_List=null;

User[] users=null;

ChatServer chatServer=null;

RoomGroup(int roomCount,Choice room_Choice,

ChatServer chatServer) {

this.chatServer=chatServer;

this.room_Choice=room_Choice;

this.users=users;

this.users_List=users_List;

rooms=new Room[roomCount];

try{

newRoom(DEFAULT_ROOM_SUBJECT);

}catch(Exception e){

System.out.println(e);

}

}// contructor 1 ended

void setUsers(User[] users) {

this.users=users;

}

void refreshList() {

chatServer.handleEvent(new Event(this, Event.ACTION_EVENT, null));

}

public int getRoomID(String roomName){

for(int i=0;i< rooms.length;i++) {

if ((rooms[i]!=null)&&(rooms[i].getRoomName().equals(roomName))) {

return i;

}

}

return 0;

}// method ’getRoomID’ ended

public int newRoom(String subject) throws TooManyRoomsException {

int i=0;

while((i< rooms.length)&&(rooms[i]!=null)) {

i++;

}

if (i< rooms.length) {

rooms[i]=new Room(subject,i);

room_Choice.addItem(subject);

room_Choice.repaint();

return i;

}else {

throw new TooManyRoomsException();

}

} // method ’newRoom’ ended

public void join(int roomID,User user) throws NoSuchRoomException {

try{

rooms[roomID].join(user);

room_Choice.select(getRoomName(roomID));

refreshList();

}catch(ArrayIndexOutOfBoundsException e){

throw new NoSuchRoomException();

}catch(NullPointerException e) {

throw new NoSuchRoomException();

}

} // method ’join’ ended

public void quit(int roomID,User user) throws NoSuchRoomException {

try{

try{

rooms[roomID].quit(user);

room_Choice.select(getRoomName(roomID));

refreshList();

}catch(NotMemberException e){

}finally{

if ((rooms[roomID].getUserCount()==0)&&

(roomID!=DEFAULT_ROOM_ID)) {

room_Choice.remove(rooms[roomID].getRoomName());

rooms[roomID]=null;

}

}

}catch(ArrayIndexOutOfBoundsException e){

throw new NoSuchRoomException();

}catch(NullPointerException e) {

throw new NoSuchRoomException();

}

} // method ’quit’ ended

public String[] getUsersName(int roomID)

throws NoSuchRoomException {

try{

if (rooms[roomID]!=null)

return rooms[roomID].getUsersName();

else return null;

}catch(ArrayIndexOutOfBoundsException e) {

throw new NoSuchRoomException();

}

} // method ’getUsersName’ ended

public int getUserCount(int roomID) throws NoSuchRoomException {

try{

return rooms[roomID].getUserCount();

}catch(ArrayIndexOutOfBoundsException e) {

throw new NoSuchRoomException();

}catch(NullPointerException e) {

throw new NoSuchRoomException();

}

} // method ’getUserCount’ ended

public String getRoomName(int roomID) throws NoSuchRoomException {

try{

return rooms[roomID].getRoomName();

}catch(ArrayIndexOutOfBoundsException e) {

throw new NoSuchRoomException();

}catch(NullPointerException e) {

throw new NoSuchRoomException();

}

} // method ’getRoomName’ ended

public boolean isActive(int roomID) throws NoSuchRoomException {

try{

if (rooms[roomID]!=null)

return true;

else return false;

}catch(ArrayIndexOutOfBoundsException e) {

throw new NoSuchRoomException();

}

}//method ’isActive’ ended

public int roomCount() {

return rooms.length;

} //method ’roomCount’ ended

} // class ’RoomGroup’ ended

// class 3:’Room’,RoomGroup引用的数据类

class Room {

String subject=null;

int ID=0;

Vector users=null;

Room(String s,int ID) {

subject=s;

this.ID=ID;

users=new Vector(10,5);

}// constructor 1 ended

public void join(User user) {

users.addElement(user);

user.roomID=ID;

user.paused=false;

}// method ’join’ ended

public void quit(User user) throws NotMemberException {

if (users.contains(user)) {

users.removeElement(user);

}else

throw new NotMemberException();

}// method ’quit’ ended

public String[] getUsersName() {

String[] usersName=null;

if (users.size()!=0) {

usersName=new String[users.size()];

for(int i=0;i< usersName.length;i++) {

usersName[i]=((User)(users.elementAt(i))).name;

}

return usersName;

}else {

return null;

}

}

// method ’getUsersName’ ended

public String getRoomName() {

return subject;

}// method ’getRoomName’ ended

public int getUserCount() {

return users.size();

}// method ’getUserCount’ ended

} // class ’Room’ ended

//class 4:’User’,服务器引用的用户服务线程类

class User extends Thread {

String name=null;

int ID=0;

boolean paused=false;

boolean used=false;

boolean belongRoom=false;

boolean logged=false;

DataInputStream io_in=null;

DataOutputStream io_out=null;

int roomID=0;

ServerSocket ss=null;

Socket s=null;

User[] users=null;

RoomGroup roomGroup=null;

TextArea message_TextArea=null;

ChatServer chatServer=null;

User(ChatServer chatServer,int ID) {

this.message_TextArea=chatServer.message_TextArea;

this.ss=chatServer.ss;

this.users=chatServer.users;

this.roomGroup=chatServer.roomGroup;

this.ID=ID;

this.chatServer=chatServer;

}// CONSTRATOR 1 ended

public void run() {

used=false;

try{

while(true){

String message=null;

s=ss.accept();

message_TextArea.appendText("\nChannel "+ID+" socket opened");

io_in=new DataInputStream(s.getInputStream());

io_out=new DataOutputStream(s.getOutputStream());

used=true;

logged=false;

try{

while(used) {

message=io_in.readUTF();

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):receive:"+message);

chatServer.num_Line++;

if (chatServer.num_Line >150) {

message_TextArea.setText("");

chatServer.num_Line=0;

}

if (message.equals("text")) sendText();

else if (message.equals("draw")) sendDraw();

else {

commandCenter(message,new DataBag("none","none",0,0,0,0,0));

if

((message.equals("new"))|(message.equals("join")))

refreshToAll();

else if (message.equals("quit")) refreshToAllNotMe();

}

}

}catch(PleaseCloseSocketException e) {

}catch(IOException e){

}finally {

try{

roomGroup.quit(roomID,this);

refreshToAllNotMe();

}catch(Exception e){}

socketClose();

used=false;

logged=false;

}

}

}catch(IOException e){

System.out.println(e);

}

}// method ’run’ ended

public void destroy() {

socketClose();

this.stop();

}// method ’destroy’ ended

public void socketClose() {

try{

io_in.close();

io_in=null;

}catch(Exception e){}

try{

io_out.flush();

io_out.close();

io_out=null;

}catch(Exception e){}

try{

s.close();

s=null;

}catch(Exception e){}

}// method ’socketClose’ ended

public void sendText() throws PleaseCloseSocketException {

String message=null;

DataBag data=null;

try{

synchronized (this){

message=io_in.readUTF();

}

data=new DataBag(name,message,0,0,0,0,0);

for(int i=0;i< users.length;i++) {

if ((users[i].logged)&&(users[i].ID!=ID)&&

(!users[i].paused)&&(users[i].roomID==roomID))

users[i].commandCenter("server_ask_text",data);

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’sendText’ ended

public void sendDraw() throws PleaseCloseSocketException {

String message=null;

int color=0,x0=0,y0=0,x1=0,y1=0;

DataBag data=null;

try{

synchronized (this) {

color=io_in.readInt();

x0=io_in.readShort();

y0=io_in.readShort();

x1=io_in.readShort();

y1=io_in.readShort();

}

data=new DataBag("none","none",color,x0,y0,x1,y1);

for(int i=0;i< users.length;i++) {

if ((users[i].logged)&&(users[i].ID!=ID)

&&(!users[i].paused)&&(users[i].roomID==roomID))

users[i].commandCenter("server_ask_draw",data);

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’sendDraw’ ended

public void refreshToAll() throws PleaseCloseSocketException {

for(int i=0;i< users.length;i++) {

if (users[i].logged)

users[i].commandCenter("server_ask_refresh",new

DataBag("none","none",0,0,0,0,0));

}

}// method "refreshToAll" ended

public void refreshToAllNotMe() throws PleaseCloseSocketException {

for(int i=0;i< users.length;i++) {

if ((users[i].logged)&&(users[i].ID!=ID))

users[i].commandCenter("server_ask_refresh",

new DataBag("none","none",0,0,0,0,0));

}

}// method "refreshToAll" ended

public void commandCenter(String command,

DataBag data) throws

PleaseCloseSocketException {

if (command.equals("server_ask_refresh")) server_ask_refresh();

else if (command.equals("refresh")) server_answer_refresh();

else if (command.equals("log")) server_answer_log();

else if (command.equals("join")) server_answer_join();

else if (command.equals("pause")) server_answer_pause();

else if (command.equals("continue")) server_answer_continue();

else if (command.equals("new")) server_answer_newRoom();

else if (command.equals("quit")) server_answer_quit();

else if (command.equals("server_ask_text")) server_ask_text(data);

else if (command.equals("server_ask_draw")) server_ask_draw(data);

}// method commandCenter ended

private void server_ask_draw(DataBag data) throws

PleaseCloseSocketException {

String message=null;

try{

synchronized (this){

io_out.writeUTF("draw");

io_out.writeInt(data.color);

io_out.writeShort(data.x0);

io_out.writeShort(data.y0);

io_out.writeShort(data.x1);

io_out.writeShort(data.y1);

}

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):send:"+"draw data");

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’server_ask_draw’ ended

private void server_ask_text(DataBag data) throws

PleaseCloseSocketException {

String message=null;

try{

synchronized (this){

io_out.writeUTF("text");

io_out.writeUTF(data.name);

io_out.writeUTF(data.message);

}

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):send:"+"text :"+data.message);

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’server_ask_text’ ended

private void server_ask_refresh() throws

PleaseCloseSocketException {

String message=null;

try{

synchronized (this){

io_out.writeUTF("refresh");

for(int i=0;i< roomGroup.roomCount();i++) {

if (roomGroup.isActive(i)) {

io_out.writeUTF(roomGroup.getRoomName(i));

String[] names=roomGroup.getUsersName(i);

if (names!=null)

for(int j=0;j< names.length;j++) {

io_out.writeUTF(names[j]);

}

io_out.writeUTF("complete");

}

}

io_out.writeUTF("ok");

io_out.writeUTF(roomGroup.getRoomName(roomID));

}

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):send:"+"refresh data");

}catch(IOException e){

throw new PleaseCloseSocketException();

}catch(NoSuchRoomException e){}

}// method ’server_ask_refresh’ ended

private void server_answer_refresh() throws

PleaseCloseSocketException {

String message=null;

commandCenter("server_ask_refresh",

new DataBag("none","none",0,0,0,0,0));

}// method ’server_answer_refresh’ ended

private void server_answer_log() throws

PleaseCloseSocketException {

String message=null;

try{

name=io_in.readUTF();

logged=true;

belongRoom=false;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):logged");

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’server_answer_log’ ended

private void server_answer_join() throws

PleaseCloseSocketException {

String message=null;

int roomID=0;

try{

if (belongRoom) {

try{

roomGroup.quit(this.roomID,this);

belongRoom=false;

}catch(NoSuchRoomException e){}

}

roomID=roomGroup.getRoomID(io_in.readUTF());

paused=false;

try{

roomGroup.join(roomID,this);

belongRoom=true;

paused=false;

this.roomID=roomID;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):join room("+roomGroup.getRoomName(roomID)

+")");

}catch(NoSuchRoomException e){}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’server_answer_join’ ended

private void server_answer_pause() throws

PleaseCloseSocketException {

String message=null;

paused=true;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):paused");

}// method ’server_answer_pause’ ended

private void server_answer_continue() throws

PleaseCloseSocketException {

String message=null;

paused=false;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):continued");

}// method ’server_answer_continue’ ended

private void server_answer_newRoom() throws

PleaseCloseSocketException {

String message=null;

int roomID=0;

try{

if (belongRoom) {

roomGroup.quit(this.roomID,this);

belongRoom=false;

}

message=io_in.readUTF();

try{

roomID=roomGroup.newRoom(message);

belongRoom=false;

paused=false;

roomGroup.join(roomID,this);

belongRoom=true;

this.roomID=roomID;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):new room("+message+")");

}catch(TooManyRoomsException e){

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}catch(NoSuchRoomException e){}

}// method ’server_answer_new’ ended

private void server_answer_quit() throws

PleaseCloseSocketException {

String message=null;

try{

try{

roomGroup.quit(roomID,this);

used=false;

logged=false;

belongRoom=false;

roomID=0;

name=null;

message_TextArea.appendText("\nChannel"+ID+"("+

name+"):quited");

}catch(NoSuchRoomException e){

io_out.writeUTF("fail");

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’server_answer_quit’ ended

}// class ’user’ ended

/*

A basic extension of the java.awt.Dialog class

*/

//class 5:QuitDialg,服务器退出对话框

public class QuitDialog extends Dialog {

void yesButton_Clicked(Event event) {

getParent().handleEvent(new Event(this,

Event.WINDOW_DESTROY, null));

}

void noButton_Clicked(Event event) {

//{{CONNECTION

// Clicked from noButton Hide the Dialog

hide();

//}}

}

public QuitDialog(Frame parent, boolean modal) {

super(parent, modal);

//setIconImage(icon);

//{{INIT_CONTROLS

setLayout(null);

addNotify();

resize(insets().left + insets().right + 337,

insets().top + insets().bottom + 135);

yesButton = new java.awt.Button(" Yes ");

yesButton.reshape(insets().left + 72,

insets().top + 84,79,22);

yesButton.setFont(new Font("Dialog", Font.BOLD, 12));

add(yesButton);

noButton = new java.awt.Button(" No ");

noButton.reshape(insets().left + 180,

insets().top + 84,79,22);

noButton.setFont(new Font("Dialog", Font.BOLD, 12));

add(noButton);

label1 = new java.awt.Label("Do you really want to quit?");

label1.reshape(insets().left + 84,insets().top + 36,180,23);

add(label1);

setTitle("Chat&WhiteBoard Server 1.0 - Quit");

setResizable(false);

//}}

}

public QuitDialog(Frame parent, String title, boolean modal) {

this(parent, modal);

setTitle(title);

}

public synchronized void show() {

Rectangle bounds = getParent().bounds();

Rectangle abounds = bounds();

move(bounds.x + (bounds.width - abounds.width)/ 2,

bounds.y + (bounds.height - abounds.height)/2);

super.show();

}

public boolean handleEvent(Event event) {

if(event.id == Event.WINDOW_DESTROY) {

hide();

return true;

}

if (event.target == noButton && event.id

== Event.ACTION_EVENT) {

noButton_Clicked(event);

}

if (event.target == yesButton && event.id

== Event.ACTION_EVENT) {

yesButton_Clicked(event);

}

return super.handleEvent(event);

}

//{{DECLARE_CONTROLS

java.awt.Button yesButton;

java.awt.Button noButton;

java.awt.Label label1;

//}}

}

/*

A basic extension of the java.awt.Dialog class

*/

//class 6:"AboutDialog",服务器"关于"对话框

public class AboutDialog extends Dialog {

void okButton_Clicked(Event event) {

//{{CONNECTION

// Clicked from okButton Hide the Dialog

hide();

//}}

}

public AboutDialog(Frame parent, boolean modal) {

super(parent, modal);

//setIconImage(icon);

//{{INIT_CONTROLS

setLayout(null);

addNotify();

resize(insets().left + insets().right + 249,

insets().top + insets().bottom + 170);

label1 = new java.awt.Label("Chat&WhiteBoard

Server 1.0",Label.CENTER);

label1.reshape(insets().left + 36,

insets().top + 12,166,21);

add(label1);

label2 = new java.awt.Label("清华大学汽车工程系",Label.CENTER);

label2.reshape(insets().left + 36,insets().top + 36,168,24);

add(label2);

okButton = new java.awt.Button("OK");

okButton.reshape(insets().left + 96,insets().top + 132,66,27);

add(okButton);

label3 = new java.awt.Label("学生科学技术协会制作",Label.CENTER);

label3.reshape(insets().left + 36,insets().top + 60,168,24);

add(label3);

label4 = new java.awt.Label("(c)版权所有",Label.CENTER);

label4.reshape(insets().left + 24,insets().top + 84,192,24);

add(label4);

label5 = new java.awt.Label("1998年8月",Label.CENTER);

label5.reshape(insets().left + 72,insets().top + 108,96,24);

add(label5);

setTitle("About");

setResizable(false);

//}}

}

public AboutDialog(Frame parent, String title, boolean modal) {

this(parent, modal);

setTitle(title);

}

public synchronized void show() {

Rectangle bounds = getParent().bounds();

Rectangle abounds = bounds();

move(bounds.x + (bounds.width - abounds.width)/ 2,

bounds.y + (bounds.height - abounds.height)/2);

super.show();

}

public boolean handleEvent(Event event) {

if(event.id == Event.WINDOW_DESTROY) {

hide();

return true;

}

if (event.target == okButton && event.id == Event.ACTION_EVENT) {

okButton_Clicked(event);

}

return super.handleEvent(event);

}

//{{DECLARE_CONTROLS

java.awt.Label label1;

java.awt.Label label2;

java.awt.Button okButton;

java.awt.Label label3;

java.awt.Label label4;

java.awt.Label label5;

//}}

}

/*

A basic extension of the java.applet.Applet class

*/

//class 7:ChatApplet,电子白板客户端Applet核心类

public class ChatApplet extends Applet implements Runnable {

volatile boolean logged=false;

volatile boolean belongRoom=false;

volatile boolean paused=false;

volatile boolean roomDataValid=false;

URL url=null;

int port=0;

Socket s=null;

Color currentColor=null;

int x0=0,y0=0,x1=0,y1=0;

String userName=null;

String roomName=null;

int roomID=0;

Rooms rooms=null;

Thread thread=null;

DataInputStream io_in=null;

DataOutputStream io_out=null;

DataOutputStream piped_out=null;

SendDrawData sender=null;

int logo_x=0,logo_y=0,logo_width=0,logo_height=0;

public void start() {

if (thread==null) {

thread=new Thread(this);

thread.start();

}

}

// method ’start’ ended

public void stop() {

if (thread!=null) {

quitButton_Clicked(new Event(this,0,null));

message_TextArea.setText("");

thread.stop();

thread=null;

}

}

//method ’stop’ ended

public void destroy() {

try{

commandCenter("ask_quit",new DataBag("none","none",0,0,0,0,0));

if (sender!=null) {

sender.quit();

sender=null;

}

socketClose();

}catch(Exception e){

System.out.println(e);

}

}

//method ’destroy’ close

public void socketClose() {

try{

piped_out.flush();

piped_out.close();

piped_out=null;

}catch(IOException e){

System.out.println(e);

}

try{

io_in.close();

io_in=null;

}catch(Exception e){}

try{

io_out.flush();

io_out.close();

io_out=null;

}catch(Exception e){}

try{

s.close();

s=null;

}catch(Exception e){}

}

//method ’socketClose’ ended

public void commandCenter(String command,

DataBag data) throws PleaseCloseSocketException {

if (command.equals("ask_refresh")) client_ask_refresh();

else if (command.equals("refresh")) client_answer_refresh();

else if (command.equals("ask_log")) client_ask_log();

else if (command.equals("ask_join")) client_ask_join();

else if (command.equals("ask_pause")) client_ask_pause();

else if (command.equals("ask_continue")) client_ask_continue();

else if (command.equals("ask_new")) client_ask_newRoom();

else if (command.equals("ask_quit")) client_ask_quit();

else if (command.equals("ask_text")) client_ask_text(data);

else if (command.equals("text")) client_answer_text();

else if (command.equals("draw")) client_answer_draw();

}// method commandCenter ended

private void client_answer_text() throws PleaseCloseSocketException{

String message=null;

String name=null;

DataBag data=null;

try{

name=io_in.readUTF();

message=io_in.readUTF();

data=new DataBag(name,message,0,0,0,0,0);

printChat_Area(data);

}catch(IOException e){

message_TextArea.appendText("抱歉,无法正确接受句子");

throw new PleaseCloseSocketException();

}

}//method ’client_answer_text’ ended

private void client_answer_draw()

throws PleaseCloseSocketException {

DataBag data=null;

int color=0,x0=0,y0=0,x1=0,y1=0;

try{

color=io_in.readInt();

x0=io_in.readShort();

y0=io_in.readShort();

x1=io_in.readShort();

y1=io_in.readShort();

data=new DataBag("none","none",color,x0,y0,x1,y1);

drawBoard_Canvas(data);

}catch(IOException e){

message_TextArea.appendText("抱歉,无法正确接受图形");

throw new PleaseCloseSocketException();

}

}//mwthod ’client_answer_draw’ ended

private void client_ask_text(DataBag data)

throws PleaseCloseSocketException {

String message=null;

try{

synchronized(sender){

io_out.writeUTF("text");

io_out.writeUTF(data.message);

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_text’ ended

private void client_ask_refresh()

throws PleaseCloseSocketException {

try{

if (sender!=null) {

synchronized(sender){

io_out.writeUTF("refresh");

}

}else{

io_out.writeUTF("refresh");

}

}catch(IOException e){

roomDataValid=false;

throw new PleaseCloseSocketException();

}

}// method ’client_ask_refresh’ ended

private void client_answer_refresh()

throws PleaseCloseSocketException {

String message=null;

RoomData roomData=null;

int roomID=0;

rooms=new Rooms(10,5);

try{

while(!((message=io_in.readUTF()).equals("ok"))) {

roomData=new RoomData(message);

rooms.addElement(roomData);

while(!(message=io_in.readUTF()).equals("complete")) {

roomData.addUser(message);

}

}

message=io_in.readUTF();

rooms.setDefault(message);

roomDataValid=true;

refreshRoomList();

}catch(IOException e){

roomDataValid=false;

message_TextArea.appendText("\n抱歉,服务器无法请求刷新数据");

throw new PleaseCloseSocketException();

}

}// method ’client_answer_refresh’ ended

private void client_ask_log() throws

PleaseCloseSocketException {

String message=null;

try{

synchronized(sender){

io_out.writeUTF("log");

io_out.writeUTF(userName);

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_log’ ended

private void client_ask_join() throws PleaseCloseSocketException {

String message=null;

int ID=0;

String subject=null;

int index=0;

try{

subject=room_Choice.getSelectedItem();

synchronized(sender){

io_out.writeUTF("join");

io_out.writeUTF(subject);

}

roomName=subject;

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_join’ ended

private void client_ask_pause()

throws PleaseCloseSocketException {

String message=null;

try{

synchronized(sender){

io_out.writeUTF("pause");

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_pause’ ended

private void client_ask_continue()

throws PleaseCloseSocketException {

String message=null;

try{

synchronized(sender){

io_out.writeUTF("continue");

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_continue’ ended

private , ;void client_ask_newRoom()

throws PleaseCloseSocketException {

String message=null;

String subject=null;

int roomID=0;

RoomData roomData=null;

try{

synchronized(sender){

io_out.writeUTF("new");

subject=subject_TextField.getText();

io_out.writeUTF(subject);

}

this.roomName=subject;

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_new’ ended

private void client_ask_quit()

throws PleaseCloseSocketException {

String message=null;

int index=0;

try{

synchronized(sender){

io_out.writeUTF("quit");

}

if (sender!=null) {

sender.quit();

sender=null;

}

}catch(IOException e){

throw new PleaseCloseSocketException();

}

}// method ’client_ask_quit’ ended

public void openSocket() throws IOException {

s=new Socket(url.getHost(),port);

io_in=new DataInputStream(s.getInputStream());

io_out=new DataOutputStream(s.getOutputStream());

}

// method openSocket ended

public void run() {

String message=null;

while(true) {

try{

if (s!=null) {

commandCenter("ask_refresh",

new DataBag("none","none",0,0,0,0,0));

}

while(logged){

message=io_in.readUTF();

commandCenter(message,new DataBag

("none","none",0,0,0,0,0));

}

synchronized (this) {

try{

wait();

}catch(Exception w){

System.out.println(w);

}

}

}catch(PleaseCloseSocketException e) {

}catch(IOException e) {}

}

}// method ’run’ ended

public void addNewChoice() {

remove(room_Choice);

room_Choice = new java.awt.Choice();

add(room_Choice);

room_Choice.reshape(60,45,132,23);

room_Choice.setFont(new Font("Dialog", Font.PLAIN, 13));

}

public void refreshRoomList() {

RoomData room=null;

if(rooms!=null) {

addNewChoice();

for(int i=0;i< rooms.size();i++) {

room=(RoomData)(rooms.elementAt(i));

room_Choice.addItem(room.roomName);

}

if (room_Choice.countItems()!=0) {

room_Choice.select(rooms.getDefaultRoomName());

refreshUserList();

}

}

}//method refreshRoomData ended

public void refreshUserList() {

String name=null;

RoomData room=null;

String names[]=null;

if (room_Choice.countItems()!=0) {

name=room_Choice.getSelectedItem();

room=rooms.getRoomByName(name);

if (room!=null) {

users_List.clear();

names=room.getUsersName();

if (names!=null) {

for(int i=0;i< names.length;i++) {

users_List.addItem(names[i]);

}

}

}

}

}// method refreshRoomData ended

public void printChat_Area(DataBag data) {

String message=null;

message=data.name+’:’+data.message;

if (!(message.trim()).endsWith("\n")) {

message=message+’\n’;

}

synchronized(this){

chat_TextArea.appendText(message);

}

}

public void sendText() {

String message=null;

DataBag data=null;

message=chat_TextField.getText();

if ((!(message.equals("")))&&

(!Character.isISOControl(message.charAt(0)))) {

try{

data=new DataBag(userName,message,0,0,0,0,0);

commandCenter("ask_text",data);

chat_TextField.setText("");

printChat_Area(data);

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("抱歉,不能发送句子");

}

}

}

synchronized public void drawBoard_Canvas(DataBag data) {

Color color=null;

Graphics g=null;

color=new Color(data.color);

g=board_Canvas.getGraphics();

g.setColor(color);

g.drawLine(data.x0,data.y0,data.x1,data.y1);

}

//To do with User Event

synchronized void chatButton_Clicked(Event event) {

sendText();

}

synchronized void chatTextField_EnterHit(Event event) {

sendText();

}

//change draw color

void colorCanvas12_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.black);

currentColor_Canvas.repaint();//}}

currentColor=Color.black;

}

void colorCanvas11_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.cyan);

currentColor_Canvas.repaint();//}}

currentColor=Color.cyan;

}

void colorCanvas10_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.blue);

currentColor_Canvas.repaint();

currentColor=Color.blue;

}

void colorCanvas9_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.gray);

currentColor_Canvas.repaint();//}}

currentColor=Color.gray;

}

void colorCanvas8_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.green);

currentColor_Canvas.repaint();//}}

currentColor=Color.green;

}

void colorCanvas7_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.lightGray);

currentColor_Canvas.repaint();//}}

currentColor=Color.lightGray;

}

void colorCanvas6_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.magenta);

currentColor_Canvas.repaint();//}}

currentColor=Color.magenta;

}

void colorCanvas5_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.orange);

currentColor_Canvas.repaint();//}}

currentColor=Color.orange;

}

void colorCanvas4_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.pink);

currentColor_Canvas.repaint();//}}

currentColor=Color.pink;

}

void colorCanvas3_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.red);

currentColor_Canvas.repaint();//}}

currentColor=Color.red;

}

void colorCanvas2_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.yellow);

currentColor_Canvas.repaint();//}}

currentColor=Color.yellow;

}

void colorCanvas1_MouseDown(Event event) {

currentColor_Canvas.setBackground(Color.white);

currentColor_Canvas.repaint();//}}

currentColor=Color.white;

}

void boardCanvas_MouseMove(Event event) {

x0=event.x;

y0=event.y;

}

void boardCanvas_MouseDrag(Event event) {

DataBag data=null;

if (sender.sended) {

x1=event.x;

y1=event.y;

data=new DataBag("none","none",currentColor.getRGB(),

x0,y0,x1,y1);

drawBoard_Canvas(data);

try{

synchronized (this){

piped_out.writeInt(currentColor.getRGB());

piped_out.writeInt(x0);

piped_out.writeInt(y0);

piped_out.writeInt(x1);

piped_out.writeInt(y1);

}

x0=x1;

y0=y1;

}catch(IOException e1){

message_TextArea.appendText("\n发送图形管道失败");

}

}

}

void newButton_Clicked(Event event) {

if (logged){

try{

commandCenter("ask_new",

new DataBag("none","none",0,0,0,0,0));

message_TextArea.appendText("\n您已经创建了该交谈室");

message_TextArea.appendText("\n现在您可以进行绘画和交谈");

message_TextArea.appendText("\n您已经加入了该交谈室");

belongRoom=true;

paused=false;

quit_Button.enable();

chat_TextField.enable();

chat_Button.enable();

board_Canvas.enable();

pause_Button.enable();

log_Button.disable();

chat_TextField.setText("请在这里输入谈话句子。");

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("\n抱歉,无法创建交谈室");

}

}

}

void subjectTextField_KeyRelease(Event event) {

String message=null;

message=subject_TextField.getText();

if ((!(message.equals("")))&&(message.charAt(0)!=’ ’)) {

new_Button.enable();

}else {

log_Button.disable();

}

}

void quitButton_Clicked(Event event) {

try{

if (logged) {

commandCenter("ask_quit",new DataBag("none",

"none",0,0,0,0,0));

logged=false;

belongRoom=false;

log_Button.disable();

join_Button.disable();

quit_Button.disable();

chat_TextField.disable();

chat_Button.disable();

board_Canvas.disable();

pause_Button.disable();

new_Button.disable();

room_Choice.disable();

chat_TextField.setText("");

chat_TextArea.setText("");

userName_TextField.setText("");

subject_TextField.setText("");

board_Canvas.repaint();

users_List.clear();

addNewChoice();

belongRoom=false;

socketClose();

message_TextArea.appendText("\n您已经退出交谈室");

}

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("\n抱歉,无法退出交谈室");

}

}

void joinButton_Clicked(Event event) {

int roomID=0;

try{

if ((logged&&roomDataValid)&&

(room_Choice.getSelectedItem()!=null)) {

commandCenter("ask_join",

new DataBag("none","none",0,0,0,0,0));

belongRoom=true;

paused=false;

message_TextArea.appendText("\n您已经进入该交谈室");

message_TextArea.appendText("\n现在您可以进行绘画和交谈");

quit_Button.enable();

chat_TextField.enable();

chat_Button.enable();

board_Canvas.enable();

pause_Button.enable();

log_Button.disable();

chat_TextField.setText("请在这里输入谈话句子。");

}

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("\n您不能进入该交谈室");

}

}

void pauseButton_Clicked(Event event) {

if (logged&&belongRoom) {

if (!paused) {

try{

commandCenter("ask_pause",

new DataBag("none","none",0,0,0,0,0));

pause_Button.setLabel("继续交谈");

message_TextArea.appendText("\n已经暂停交谈");

paused=true;

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("\n无法暂停交谈");

}

}else {

try{

commandCenter("ask_continue",

new DataBag("none","none",0,0,0,0,0));

pause_Button.setLabel("暂停交谈");

message_TextArea.appendText("\n继续交谈");

paused=false;

}catch(PleaseCloseSocketException e){

message_TextArea.appendText("\n无法继续交谈");

}

}

}

}

void roomChoice_Action(Event event) {

refreshUserList();

}

void userNameTextField_KeyRelease(Event event) {

String message=null;

message=userName_TextField.getText();

if ((message!=null)&&(!(message.equals("")))

&&(message.charAt(0)!=’ ’)&&(logged==false)) {

log_Button.enable();

userName=message;

}else {

log_Button.disable();

userName=null;

}

}

void logButton_Clicked(Event event) {

PipedInputStream in=null;

PipedOutputStream out=null;

try{

openSocket();

in=new PipedInputStream();

out=new PipedOutputStream(in);

piped_out=new DataOutputStream(out);

sender=new SendDrawData(in,io_out);

try{

commandCenter("ask_log",

new DataBag("none","none",0,0,0,0,0));

logged=true;

belongRoom=false;

synchronized(this){

notify();

}

message_TextArea.appendText("\n登录成功.");

}catch(PleaseCloseSocketException e) {

socketClose();

message_TextArea.appendText("\n抱歉,无法登录");

}

try{

commandCenter("ask_refresh",new DataBag("none","none",0,0,0,0,0));

join_Button.enable();

new_Button.enable();

quit_Button.enable();

subject_TextField.enable();

room_Choice.enable();

message_TextArea.appendText

("\n请选择交谈室,\n并点击\"加入交谈室\"钮");

}catch(PleaseCloseSocketException e) {

socketClose();

message_TextArea.appendText("\n抱歉,无法刷新交谈室数据");

}

}catch(IOException e){

message_TextArea.appendText("\n抱歉,无法与服务器取得联系");

socketClose();

}

}

void cleanButton_Clicked(Event e){

board_Canvas.repaint();

}

public void init() {

super.init();

port=Integer.parseInt(getParameter("port"));

url=getCodeBase();

logo_Image=getImage(url,imageFileName);

MediaTracker tracker = new MediaTracker(this);

tracker.addImage(logo_Image,0);

try{

tracker.waitForAll();

}catch(Exception e){

System.out.println(e);

}

logo_width=logo_Image.getWidth(this);

logo_height=logo_Image.getHeight(this);

//{{INIT_CONTROLS

setLayout(null);

addNotify();

resize(610,375+70);

repaint();

setBackground(Color.lightGray);

logo =new Logo(logo_Image,460,60);

logo_x=(size().width-logo_width)/2;

logo_y=(70-logo_height)/2+377;

logo.setBackground(Color.lightGray);

logo.reshape(logo_x,logo_y,logo_width,logo_height);

add(logo);

chat_Button = new java.awt.Button("发送");

chat_Button.disable();

chat_Button.reshape(552,340,48,24);

add(chat_Button);

log_Button = new java.awt.Button("登录");

log_Button.reshape(144,5,48,24);

add(log_Button);

log_Button.disable();

join_Button = new java.awt.Button("加入交谈室");

join_Button.reshape(12,77,72,24);

add(join_Button);

join_Button.disable();

new_Button = new java.awt.Button("创建交谈室");

new_Button.reshape(12,150,72,24);

add(new_Button);

new_Button.disable();

pause_Button = new java.awt.Button("暂停交谈");

pause_Button.reshape(108,150,72,24);

add(pause_Button);

pause_Button.disable();

quit_Button = new java.awt.Button("退出交谈室");

quit_Button.reshape(108,77,72,24);

add(quit_Button);

quit_Button.disable();

userName_Label = new java.awt.Label("用户名",Label.CENTER);

userName_Label.reshape(0,5,60,24);

userName_Label.setBackground(Color.lightGray);

add(userName_Label);

userName_TextField = new java.awt.TextField();

userName_TextField.reshape(60,5,72,24);

userName_TextField.setFont

(new Font("TimesRoman", Font.PLAIN, 13));

add(userName_TextField);

room_Choice = new java.awt.Choice();

add(room_Choice);

room_Choice.disable();

room_Choice.reshape(60,45,132,23);

room_Choice.setFont(new Font("Dialog", Font.PLAIN, 13));

room_Label = new java.awt.Label("交谈室",Label.CENTER);

room_Label.reshape(0,45,60,24);

room_Label.setBackground(Color.lightGray);

add(room_Label);

users_List = new java.awt.List(5,false);

add(users_List);

users_List.reshape(12,212,180,60);

users_List.setFont(new Font("Dialog", Font.PLAIN, 13));

subject_TextField = new java.awt.TextField();

subject_TextField.reshape(80,115,112,24);

subject_TextField.setFont(new Font("TimesRoman",

Font.PLAIN, 13));

add(subject_TextField);

subject_TextField.disable();

subject_Label = new java.awt.Label("新交谈室主题",Label.CENTER);

subject_Label.reshape(0,115,72,24);

subject_Label.setBackground(Color.lightGray);

add(subject_Label);

users_Label = new java.awt.Label("当前交谈室用户",Label.CENTER);

users_Label.reshape(15,185,100,24);

users_Label.setBackground(Color.lightGray);

add(users_Label);

message_TextArea = new java.awt.TextArea();

message_TextArea.setEditable(false);

message_TextArea.appendText("\n欢迎您进入交谈室。");

message_TextArea.reshape(12,300,180,72);

message_TextArea.setFont(new Font("Dialog", Font.PLAIN, 13));

add(message_TextArea);

message_Label = new java.awt.Label("系统信息");

message_Label.reshape(12,276,88,28);

message_Label.setBackground(Color.lightGray);

add(message_Label);

board_Canvas = new java.awt.Canvas();

board_Canvas.reshape(216,36,324,216);

board_Canvas.setBackground(new Color(16777215));

add(board_Canvas);

board_Canvas.addNotify();

board_Canvas.disable();

board_Label = new java.awt.Label("绘画白板");

board_Label.reshape(216,12,100,24);

board_Label.setBackground(Color.lightGray);

add(board_Label);

clean_Button = new java.awt.Button("清除白板图画");

clean_Button.reshape(320,10,90,24);

add(clean_Button);

color_canvas1 = new java.awt.Canvas();

color_canvas1.reshape(552,36,20,24);

color_canvas1.setBackground(new Color(16777215));

add(color_canvas1);

currentColor_Canvas = new java.awt.Canvas();

currentColor_Canvas.reshape(564,228,20,24);

currentColor_Canvas.setBackground(new Color(0));

currentColor=new Color(0);

add(currentColor_Canvas);

color_Canvas12 = new java.awt.Canvas();

color_Canvas12.reshape(576,156,20,24);

color_Canvas12.setBackground(new Color(0));

add(color_Canvas12);

color_Canvas11 = new java.awt.Canvas();

color_Canvas11.reshape(552,156,20,24);

color_Canvas11.setBackground(new Color(65535));

add(color_Canvas11);

color_Canvas10 = new java.awt.Canvas();

color_Canvas10.reshape(576,132,20,24);

color_Canvas10.setBackground(new Color(255));

add(color_Canvas10);

color_Canvas9 = new java.awt.Canvas();

color_Canvas9.reshape(552,132,20,24);

color_Canvas9.setBackground(new Color(8421504));

add(color_Canvas9);

color_Canvas8 = new java.awt.Canvas();

color_Canvas8.reshape(576,108,20,24);

color_Canvas8.setBackground(new Color(65280));

add(color_Canvas8);

color_Canvas7 = new java.awt.Canvas();

color_Canvas7.reshape(552,108,20,24);

color_Canvas7.setBackground(new Color(12632256));

add(color_Canvas7);

color_Canvas6 = new java.awt.Canvas();

color_Canvas6.reshape(576,84,20,24);

color_Canvas6.setBackground(new Color(16711935));

add(color_Canvas6);

color_Canvas5 = new java.awt.Canvas();

color_Canvas5.reshape(552,84,20,24);

color_Canvas5.setBackground(new Color(16762880));

add(color_Canvas5);

color_Canvas4 = new java.awt.Canvas();

color_Canvas4.reshape(576,60,20,24);

color_Canvas4.setBackground(new Color(16756655));

add(color_Canvas4);

color_canvas3 = new java.awt.Canvas();

color_canvas3.reshape(552,60,20,24);

color_canvas3.setBackground(new Color(16711680));

add(color_canvas3);

color_Canvas2 = new java.awt.Canvas();

color_Canvas2.reshape(576,36,20,24);

color_Canvas2.setBackground(new Color(16776960));

add(color_Canvas2);

currentColor_Label = new java.awt.Label("当前色",Label.CENTER);

currentColor_Label.reshape(540,192,64,28);

currentColor_Label.setBackground(Color.lightGray);

add(currentColor_Label);

color_Label = new java.awt.Label("调色板",Label.CENTER);

color_Label.reshape(540,12,64,24);

color_Label.setBackground(Color.lightGray);

add(color_Label);

chat_TextField = new java.awt.TextField();

chat_TextField.reshape(216,340,324,24);

chat_TextField.setFont(new Font("Dialog", Font.PLAIN, 13));

add(chat_TextField);

chat_TextField.disable();

chat_TextArea = new java.awt.TextArea();

chat_TextArea.setEditable(false);

chat_TextArea.reshape(216,264,372,72);

chat_TextArea.setFont(new Font("Dialog", Font.PLAIN, 13));

add(chat_TextArea);

//}}

}

public boolean handleEvent(Event event) {

if (event.target == log_Button && event.id

== Event.ACTION_EVENT) {

logButton_Clicked(event);

return true;

}

if (event.target == userName_TextField && event.id

== Event.KEY_RELEASE) {

userNameTextField_KeyRelease(event);

return true;

}if (event.target == room_Choice && event.id

== Event.ACTION_EVENT) {

roomChoice_Action(event);

return true;

}

if (event.target == pause_Button && event.id

== Event.ACTION_EVENT) {

pauseButton_Clicked(event);

return true;

}

if (event.target == join_Button && event.id

== Event.ACTION_EVENT) {

joinButton_Clicked(event);

return true;

}

if (event.target == quit_Button && event.id

== Event.ACTION_EVENT) {

quitButton_Clicked(event);

return true;

}

if (event.target == subject_TextField && event.id

== Event.KEY_RELEASE) {

subjectTextField_KeyRelease(event);

return true;

}

if (event.target == new_Button && event.id

== Event.ACTION_EVENT) {

newButton_Clicked(event);

return true;

}

if ((event.target == board_Canvas) && (event.id

== Event.MOUSE_DRAG)

&& (logged==true)&&(paused==false)) {

event.translate(-216,-36);

boardCanvas_MouseDrag(event);

return true;

}

if (event.target == board_Canvas && event.id

== Event.MOUSE_MOVE) {

event.translate(-216,-36);

boardCanvas_MouseMove(event);

return true;

}

if (event.target == color_canvas1 && event.id

== Event.MOUSE_DOWN) {

colorCanvas1_MouseDown(event);

return true;

}

if (event.target == color_Canvas2 && event.id

== Event.MOUSE_DOWN) {

colorCanvas2_MouseDown(event);

return true;

}

if (event.target == color_canvas3 && event.id

== Event.MOUSE_DOWN) {

colorCanvas3_MouseDown(event);

return true;

}

if (event.target == color_Canvas4 && event.id

== Event.MOUSE_DOWN) {

colorCanvas4_MouseDown(event);

return true;

}

if (event.target == color_Canvas5 && event.id

== Event.MOUSE_DOWN) {

colorCanvas5_MouseDown(event);

return true;

}

if (event.target == color_Canvas6 && event.id

== Event.MOUSE_DOWN) {

colorCanvas6_MouseDown(event);

return true;

}

if (event.target == color_Canvas7 && event.id

== Event.MOUSE_DOWN) {

colorCanvas7_MouseDown(event);

return true;

}

if (event.target == color_Canvas8 && event.id

== Event.MOUSE_DOWN) {

colorCanvas8_MouseDown(event);

return true;

}

if (event.target == color_Canvas9 && event.id

== Event.MOUSE_DOWN) {

colorCanvas9_MouseDown(event);

return true;

}

if (event.target == color_Canvas10 && event.id

== Event.MOUSE_DOWN) {

colorCanvas10_MouseDown(event);

return true;

}

if (event.target == color_Canvas11 && event.id

== Event.MOUSE_DOWN) {

colorCanvas11_MouseDown(event);

return true;

}

if (event.target == color_Canvas12 && event.id

== Event.MOUSE_DOWN) {

colorCanvas12_MouseDown(event);

return true;

}

if (event.target == chat_TextField && event.id

== Event.ACTION_EVENT) {

chatTextField_EnterHit(event);

return true;

}

if (event.target == chat_Button && event.id

== Event.ACTION_EVENT) {

chatButton_Clicked(event);

return true;

}

if (event.target == clean_Button && event.id

== Event.ACTION_EVENT) {

cleanButton_Clicked(event);

return true;

}

return super.handleEvent(event);

}

//{{DECLARE_CONTROLS

java.awt.Button chat_Button;

java.awt.Button log_Button;

java.awt.Button join_Button;

java.awt.Button new_Button;

java.awt.Button pause_Button;

java.awt.Button quit_Button;

java.awt.Label userName_Label;

java.awt.TextField userName_TextField;

java.awt.Choice room_Choice;

java.awt.Label room_Label;

java.awt.List users_List;

java.awt.TextField subject_TextField;

java.awt.Label subject_Label;

java.awt.Label users_Label;

java.awt.TextArea message_TextArea;

java.awt.Label message_Label;

java.awt.Canvas board_Canvas;

java.awt.Label board_Label;

java.awt.Canvas color_canvas1;

java.awt.Canvas currentColor_Canvas;

java.awt.Canvas color_Canvas12;

java.awt.Canvas color_Canvas11;

java.awt.Canvas color_Canvas10;

java.awt.Canvas color_Canvas9;

java.awt.Canvas color_Canvas8;

java.awt.Canvas color_Canvas7;

java.awt.Canvas color_Canvas6;

java.awt.Canvas color_Canvas5;

java.awt.Canvas color_Canvas4;

java.awt.Canvas color_canvas3;

java.awt.Canvas color_Canvas2;

java.awt.Label currentColor_Label;

java.awt.Label color_Label;

java.awt.TextField chat_TextField;

java.awt.TextArea chat_TextArea;

java.awt.Button clean_Button;

Logo logo;

Image logo_Image=null;

final static String imageFileName="cdownlogo.jpg";

//}}

}

// class applet ended

//class 8:RoomData,Applet引用的交谈室数据类

class RoomData {

String roomName=null;

int roomID=0;

Vector users=null;

RoomData(String name) {

roomName=name;

users=new Vector(10,5);

}

public void addUser(String name) {

users.addElement(name);

}

public void removeUser(String name) {

users.removeElement(name);

}

public String[] getUsersName() {

Enumeration e=null;

String[] s=null;

if ((users!=null)&(users.size() >0)) {

e=users.elements();

s=new String[users.size()];

for(int i=0;i< s.length;i++) {

s[i]=(String)(users.elementAt(i));

}

}

return s;

}

public int usersCount() {

return users.size();

}

}

// class ’RoomData’ ended

//class 9:Rooms,Applet所用的交谈室数据类

class Rooms extends Vector {

String defaultSubject=null;

public Rooms(int a,int b) {

super(a,b);

defaultSubject=RoomGroup.DEFAULT_ROOM_SUBJECT;

}

public void setDefault(String name){

if (getRoomByName(name)!=null) {

defaultSubject=name;

}

}

public String getDefaultRoomName() {

return defaultSubject;

}

public int getRoomID(String name) {

int index=0;

for(int i=0;i< size();i++) {

if (((RoomData)elementAt(i)).roomName==name) {

index=((RoomData)elementAt(i)).roomID;

return index;

//break;

}

}

return -1;

}

public RoomData getRoomByName(String name) {

int index=0;

for(int i=0;i< size();i++) {

if ((((RoomData)elementAt(i)).roomName).equals(name)) {

index=i;

return ((RoomData)elementAt(i));

}

}

return null;

}

}

///class 10:DataBag,包装图形和文字信息类

class DataBag {

String message=null;

String name=null;

int color=0,x0=0,y0=0,x1=0,y1=0;

DataBag(String name,String message,int color,int x0,int y0,int x1,int y1) {

this.name=name;

this.message=message;

this.color=color;

this.x0=x0;

this.y0=y0;

this.x1=x1;

this.y1=y1;

}

}

//class 11:SendDraw,Applet后台数据传输类

class SendDrawData extends Thread {

volatile boolean sended=true;

DataInputStream io_in=null;

DataOutputStream io_out=null;

SendDrawData(PipedInputStream io_in,DataOutputStream io_out) {

super();

this.io_in=new DataInputStream(io_in);

this.io_out=io_out;

start();

}

public void run() {

int color,x0,y0,x1,y1;

try{

while(true) {

color=io_in.readInt();

x0=io_in.readInt();

y0=io_in.readInt();

x1=io_in.readInt();

y1=io_in.readInt();

sended=false;

synchronized(this) {

io_out.writeUTF("draw");

io_out.writeInt(color);

io_out.writeShort(x0);

io_out.writeShort(y0);

io_out.writeShort(x1);

io_out.writeShort(y1);

}

sended=true;

}

}catch(IOException e){

try{

io_in.close();

io_in=null;

io_out.flush();

io_out.close();

io_out=null;

}catch(IOException r){}

}

}

public void quit() {

try{

io_in.close();

io_out.flush();

io_in=null;

}catch(IOException e){

System.out.println(e);

}

stop();

}

}

//cl

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有