`
izuoyan
  • 浏览: 9222894 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
阅读更多
摘要

本文阐述了基于Linux环境,Java语言实现的基本聊天室功能,涉及Linux下的Java 语言的Socket编程。以及Java语言的多线程编程。

关键字
Linux Java Thread Socket Tcp
简介
开发背景
操作系统》和《计算机网络》的学习,使我能够有机会选择“基于Linux的网络聊天室的实现”这个课题项目,使自己课堂所学的理论能够联系实际,并且能够学习自己没有涉及过网络方面以及面向对象的基本思想,而且在编写聊天程序的过程中,也涉及到多线程的程序设计问题,这个概念在《操作系统》中已经学到,但是对于真正的语言应用却是第一次。Java语言的多线程编程提供了很好的基础类,可以很容易实现多线程的调用。以及Java语言的跨平台,使我有机会在Linux下编写程序,当然对于Linux下的Java编程和Windows下的Java编程,没有多少的不同,这些都是Java语言没有平台特有的特征带来的。
系统开发环境
Linux正以自由的精神席卷全球网络操作系统市场,而Java凭借其开放、先进的架构正迅速占领着高端软件领域。将这二者结合,便可通过Linux低廉的成本实现Java高级应用,在自由、高效的环境下充分发挥出Java的优势。因此,无论从成本还是性能上考虑,二者的结合都可谓是相得益彰。
例如,现在热门的服务器端脚本JSP的推荐实现就是Linux上的Tomcat,而与Jboss结合更是极佳的EJB平台。但是,Linux之所以未能在桌面应用等领域迅速普及,软件安装和设置复杂是一个重要原因。要在Linux下实现Java编程,其普通的环境设置可能令习惯了Windows的用户望而却步。其实,很多问题只需要简单的设置就能解决。
而对于本次课程项目的开发也正是两者的结合,尽管结合没有发挥各自的精髓,但是也能体会和感受到Java+Linux 的魅力。
技术概要
网络通信基本原理

<!--[if !supportLists]-->Ø <!--[endif]-->TCP(Transmission Control Protocol)基础

数据传输协议允许创建和维护与远程计算机的连接。连接两台计算机就可彼此进行数据传输。如果创建客户应用程序,就必须知道服务器计算机名或者IP地址(RemoteHost属性),还要知道进行侦听的端口(RemotePort属性),然后调用Connect方法。如果创建服务器应用程序,就应设置一个收听端口(LocalPort属性)并调用Listen方法。当客户计算机需要连接时就会发生ConnectionRequest事件。为了完成连接,可调用ConnectionRequest事件内的Accept方法。建立连接后,任何一方计算机都可以收发数据。为了发送数据,可调用SendData方法。当接收数据时会发生DataArrival事件。调用DataArrival事件内的GetData方法就可获取数据。

<!--[if !supportLists]-->Ø <!--[endif]-->UDP(User Datagram Protocol)基础

用户数据文报协议(UDP)是一个无连接协议。跟TCP的操作不同,计算机并不建立连接。另外UDP应用程序可以是客户机,也可以是服务器。
为了传输数据,首先要设置客户计算机的LocalPort属性。然后,服务器计算机只需将
RemoteHost设置为客户计算机的Internet地址,并将RemotePort属性设置为跟客户计算机的LocalPort属性相同的端口,并调用SendData方法来着手发送信息。于是,客户计算机使用DataArrival事件内的GetData方法来获取已发送的信息。

<!--[if !supportLists]-->Ø <!--[endif]-->Socket(Java)

套接字方式通信(socket-based communication) 通过指派套接字实现程序自己的通信。套接字(Socket) 是一种抽象,为服务器和客户之间的通信提供方便。Java处理套接字通信的方式很像处理I/O操作,这样,程序对套接字进行读写就像读写文件一样容易。

Java支持流套接字(steam socket)和数据报套接字(datagram socket)。流套接字使用TCP协议(Transmission Control Protocol, 传输控制协议)进行数据的传输,而数据报套接字使用UDP协议(User Datagram Protocol, 用户数据报协议)。因为TCP能够探测丢失的数据传输并重新提交它们,因此传输的数据不会丢失,是可靠的。相比之下,UDP协议不能保证无损失传输。所以,采用TCP协议通信可以保证数据的正确传输。

<!--[if !supportLists]-->Ø <!--[endif]-->客户/服务器模式

网络聊天室涉及的一个服务器端和N个客户端。客户向服务器发送请求,服务器对请求作出响应。客户尝试与服务器建立连接,服务器可以接受连接也可以拒绝连接。一旦连接建立起来,客户和服务器就可以通过套节字进行通信。
客户开始工作时,服务器必须正在运行,等待客户的连接请求。创建服务器和客户所需要的语句如图1-1所示。

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-1 服务器创建一个服务器套接字,与用户的连接一旦建立,就用客户套接字也客户保持连接
要建立服务器,需要创建一个服务器套接字,并把它附加到一个端口上,服务器通过这个端口监听连接请求。端口标识套接字上的TCP服务。编号在0到1023之间的端口用来为特权进程服务。
下面的语句创建一个服务器套接字server:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="1" width="55"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="38" width="550"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->

ServerSocket server = new ServerSocket(port);1

<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


<!--[if !supportFootnotes]-->[1]<!--[endif]--> 创建了一个服务器套接字之后,服务器就能够使用下面的语句监听连接请求:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="3" width="55"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="38" width="550"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->

Socket connectToClient = server.accept();

<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


这条语句一直等待,直到客户连接到服务器套接字。客户发送下面的语句与服务器建立连接:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="6" width="55"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="39" width="550"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->

Socket connectToServer = new Socket(ServerName, port);

<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


该语句用来打开一个套接字,以便客户程序能够与服务器进行通信。ServerName是服务气的Internet主机名或IP地址。
服务器接受连接之后,就用与处理I/O数据流相同的方式建立服务器与客户之间的通信。
要获得输入数据流和输出数据流,可以使用套接字对象中的getInputStream()和getOutputstream()方法:

<!--[if gte vml 1]> <table cellpadding="0" cellspacing="0"> <tbody><tr> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="60" width="550"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->

InputStream isFromServer = connectToServer.getInputStream();

OutputStream osToServer= connectToServer.getOutputSteam();
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->

InputStream 和 OutputSteam是用户读写字节。可以使用DataInputStream、DataOutputSteam、BufferReader 和 PrintWriter包装InputStream和OutputSteam,读取double、int、String之类的数据值。可以使用readLine方法读入一行数据,使用println向端口写入一行。

多线程技术编程基本原理
一个线程(Thread)是指程序中完成一个任务的有始有终的执行流。Java语言支持多个线程同时运行。如下图1-2

<!--[if gte vml 1]> <tr> <td><![endif]> <div> <p align=center style='text-align:center'><span style='mso-ascii-"Times New Roman"; mso-hansi-"Times New Roman"'>任务</span>1</p> </div> <![if !mso]></td> </tr> </table> <![endif]> <tr> <td><![endif]> <div> <p align=center style='text-align:center'><span style='mso-ascii-"Times New Roman"; mso-hansi-"Times New Roman"'>任务</span>2</p> <div>&nbsp;</div> </div> <![if !mso]></td> </tr> </table> <![endif]> <tr> <td><![endif]> <div> <p align=center style='text-align:center'><span style='mso-ascii-"Times New Roman"; mso-hansi-"Times New Roman"'>任务</span>3</p> <div>&nbsp;</div> </div> <![if !mso]></td> </tr> </table> <![endif]> <![endif]--><!--[if !vml]-->



<!--[endif]-->


图1-2 多个线程分享一个CPU
多线程可以使程序反应更快、交互性更强,并提高执行效率。Java对多线程程序设计提供更好的支持,包括内在地支持创建线程和锁定资源以避免冲突,解决了资源的共享冲突问题。
当程序运行时,Java解释器为main方法开始一个线程。此时可以创建另外的线程。每一个线程都是一个对象,它的类实现Runnable接口或者扩展实现了Runnable接口的类。也可以通过扩展Thread类来实现Runnable接口来创建线程。Thread事实实现了Runnable接口。

<!--[if !supportLists]-->Ø <!--[endif]-->线程有五种状态:新建、就绪、运行、阻塞、结束。

如图1-3所示:

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-3一个线程处于其中的一个状态
<!--[if !supportLists]-->à <!--[endif]-->新创建一个线程的时候,它进入“新建状态”。调用start方法启动线程后,它进入“就绪状态”。就绪态可以通过调用run方法实现到“运行状态”的转移。
<!--[if !supportLists]-->à <!--[endif]-->如果给定的CPU时间用完,或者调用yield()方法可以使线程处于“就绪状态”
<!--[if !supportLists]-->à <!--[endif]-->当线程执行结束,然后其自然应该进入“结束状态”。
<!--[if !supportLists]-->à <!--[endif]-->当线程因为调用sleep()等方法,将进入“阻塞状态”。此状态还可以重新进入“就绪状态”,接着重新得到运行。

<!--[if !supportLists]-->Ø <!--[endif]-->Thread类包括以下的几种控制方法:

public void run()方法,用来执行线程。用户线程类中必须覆盖该方法。
public void start()方法,它引起对run方法的调用。
public void stop()方法,结束一个线程
public void suspend()方法,挂起一个线程<!--[if !supportFootnotes]-->[2]<!--[endif]-->
public void resume()方法,唤醒一个线程<!--[if !supportFootnotes]-->[3]<!--[endif]-->

public static void sleep(long millis) throws InterruptedException 方法,可以将在运行的线程置为休眠状态,休眠时间为指定的毫秒。

public void interrupt()方法,中断正在运行的线程。
public static boolean isInterrupted()方法,测试线程是否被中断。
public boolean isAlive()方法,检查线程是不是处于运行态。
public void setPriority(int p)方法,设置方法的优先级。从1~10
public final void wait() throws InterruptedException 方法,将该线程置为暂停状态,等待另外一个线程的通知。
public final void notify()方法,唤醒一个等待的线程。
Linux下Java编程
本次课题项目采用的环境是:

<!--[if !supportLists]-->¨ <!--[endif]-->Linux Platform - J2SE(TM) and NetBeans(TM) IDE Bundle NB 4.1 / J2SE 5.0 Update 4<!--[if !supportFootnotes]-->[4]<!--[endif]-->

<!--[if !supportLists]-->¨ <!--[endif]-->Rat Hat Linux 9.0

Linux采用J2SENetBeans可以很容易的开发面向Linux的应用程序,可以移植到windows平台下运行。其中NetBean Sun公司开发的免费Java图形用户界面编辑器。(如图1-4)可以很轻松的实现界面的设计,它将控件以Swing awt JavaBean分类放置。

集成Tomcat 5.0 加入对XML,Structs的支持。这其中感触最深的地方是,不能修改自动生成的组件初始代码。假如要用ButtonGroup 就得自己去另写一个函数来初始化。感觉这样做,因为不能随意修改代码,能避免随意修改所导致的错误。但是,很多时候,我们真的要修改那部分代码反倒是件很麻烦的事了。
对于window平台有个比较不当的地方就是内存消耗太大。硬盘频繁访问。在Linux环境可以明显感觉到速度快了不少!

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-4 windows 平台下的NetBeans运行界面<!--[if !supportFootnotes]-->[5]<!--[endif]-->
基于Linux的网络聊天室的具体实现
服务器端和客户端体系结构
根据通信的基本原理,不难分析服务器端与客户端的通信实现,以下是客户端和服务器端的交互流程,如图1-5

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-5 客户端和服务器端的基本流程
流程图的简要描述
ServerClient端通信主要是服务器端创建多个线程,生成多个Socket对不同的用户进行通信。服务器端和客户端通过消息命令字的方式进行消息确认。方式是在消息头加入“命令字”。
自定义命令字含义如下:
[MESSAGE]:表示接下来的一句话是消息
[NAME]: 表示接下来的一句话是名字
[FIRSTNAME]:用于程序逻辑控制,表示第一个
[SYSTEM]: 系统消息
[Server exit!]服务器退出
[WHISPERMESSAGE]:私聊控制字
[QUIT] 表示客户端退出聊天室
客户端:
客户端由两个类实现,一个是主类ClientJFrame另外一个是用于播放声音的PlaySound类。一下是客户端的类关系图,图1-6

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-6 Client端两个类,ClientJFrame中创建PlaySound实例来播放声音

<!--[if !supportLists]-->Ø <!--[endif]--><!--[if gte vml 1]> <table cellpadding="0" cellspacing="0"> <tbody><tr> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="404" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
public void startConnect()
{
try
{

sock = new Socket(ipAddress, DEFAULT_PORT); //新建一个socket

if(sock!=null) //连接成功

{

processMsg();

}

isFromServer=new BufferedReader(); //新建一个接收变量

osToServer = new PrintWriter(); //新建一个输出变量

osToServer.println("[NAME]" + name); //发名字

osToServer.flush(); //刷新数据缓冲
}

catch(IOException ex)

{
}

readThread=new Thread(this); //通过Runnable实现

readThread.start();

}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->Client端通过方法startConnect(),尝试连接服务器端,其函数原型如下:

<!--[if !supportLists]-->Ø <!--[endif]-->通过SendInformation()方法实现数据的发送,实现函数原型如下:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="3" width="23"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="259" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
public void sendInformation()
{
if(私聊)
{

osToServer.println("[WHISPERMESSAGE]" + message);//发送私聊信息

osToServer.flush();
}
else
{

osToServer.println("[MESSAGE]" + message); //发送群聊信息

osToServer.flush();

}
}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


通过调用prinln()方法可以向端口写一句消息。

<!--[if !supportLists]-->Ø <!--[endif]-->当程序退出,或者服务器退出,线程应该结束运行。客户端,通过重载Thread的run方法实现,函数的原型如下:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="2" width="23"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="397" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
public void exit()
{
try
{
osToServer.println("[QUIT]"); //向服务器发送退出命令字

osToServer.flush();

}

catch(Exception exc){}

try
{
sock.close(); //关闭socket

isFromServer.close(); //输入缓冲关闭

osToServer.close(); //输出缓冲关闭

}
catch(IOException ioe){}
finally //不管异常是否发生都将执行
{

System.exit(0);

}
}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


<!--[if !supportLists]-->à <!--[endif]-->在退出应用程序之前,向服务器端发送[QUIT]命令字,实现聊天室的更新。

<!--[if !supportLists]-->à <!--[endif]-->客户端退出要将socket关闭,要将输入和输出的数据流关闭。

<!--[if !supportLists]-->à <!--[endif]-->最后将执行finally块程序,最后调用System的exit的函数退出应用程序。

服务器端
服务器端主要创建了5个类,其中ServerJFrame是主类,CommunicateThread用于连接客户端,BroadcastThread用与对消息的广播,WhisperThread用于处理悄悄话,BroadcastName用户广播当前在线的用户。图1-7 显示了类中的方法和类的属性。

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-7 Server端的类视图

<!--[if !supportLists]-->Ø <!--[endif]-->Server相对与客户端更加的复杂,要主动监听客户端发送的连接请求,创建不同的线程,来应答客户的请求。创建的线程,接受客户发送的数据的处理。

Server创建了连接线程后,还必须创建广播线程,将每一个客户发送的消息广播出去,到每一个客户端。对于广播线程和数据接受和处理线程之间的资源共享问题,我采用了,有序运行的方法来消除死锁。即在用户发送来数据时,开启广播线程,对刚才的数据进行广播,在信息广播结束后,关闭广播线程。这样一前一后,就可以保证数据广播的正确性。
在server还需要处理的一件事,就是如何将私聊信息发送给指定的客户端。我采用的方式是,“用户名查找发送法”,可以比较快而准确的发送数据<!--[if !supportFootnotes]-->[6]<!--[endif]-->,为了和群聊消息区分,我采用WhisperThread类单独给予处理。这样可以清晰的区分。
在客户端,还需要处理的一件事情就是,管理当前的在线用户。我使用一个堆栈<!--[if !supportFootnotes]-->[7]<!--[endif]-->来管理用户,当用户来到时,就将用户名压入堆栈。当用户退出时,将用户的名字从中去除<!--[if !supportFootnotes]-->[8]<!--[endif]-->

<!--[if !supportLists]-->Ø <!--[endif]-->服务端使用serverListen()函数开始监听端口,其函数原型如下:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="13" width="31"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="169" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
private void serverListen()
{

chatAcceptThread = new Thread(this); //创建一个监听线程

chatAcceptThread.start();

broadcastThread = new BroadcastThread(this); //创建广播线程

broadcastThread.start();

}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


<!--[if !supportLists]-->Ø <!--[endif]-->ServerJFrame是通过Runnable接口来创建的线程。其run函数实现客户端的接受并创建一个新的线程:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="5" width="31"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="398" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
public void run()
{

clients = new java.util.Vector(); //分配一个栈,用于存储用户线程

clientsInfor = new java.util.Vector(); //用于存储用户名
try
{

serverSock=new ServerSocket(DEFAULT_PORT); //新建一个Socket

}

catch(IOException e){}

try
{

while(true)

{

Socket clientSock = serverSock.accept();

CommunicateThread ct = new CommunicateThread(); //实例化通信类

boolean addSucOrNot = clients.add(ct); //将当前的进程压入堆栈

}
}

catch(IOException e){}

}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


在run函数中有一个while(true)程序块,表示这个进程将随应用进程一起存在,所以服务器端将不停的监听端口,当一个连接进入的时候,就通过CommunicateThread来处理当前连接的进程(客户端),然后,服务器主线程将监听下一个连接。

为了后面能够将数据广播出去,和实现私聊,必须要得到响应的线程,所以在向堆栈压入线程的时候,需要有一个变量(index)来指示线程, index 不会随着客户的退出而删除<!--[if !supportFootnotes]-->[9]<!--[endif]-->,而是逐次累加,那么当客户退出时,要将此进程在堆栈中的位置设置为[EMPTY],来表示一个客户端已经退出,此时,服务器端要结束和这个客户端连接的线程。


<!--[if !supportLists]-->Ø <!--[endif]-->那么当用户发来数据的时候,并且命令字为[MESSAGE]的时候,服务器需要将这条信息广播出去,这个由Broadcast来处理,其中的run函数原型如下:

<!--[if gte vml 1]> <table align="left" cellpadding="0" cellspacing="0"> <tbody><tr> <td height="6" width="31"><br></td> </tr> <tr> <td><br></td> <td style="border: 0.75pt solid black; background: white none repeat scroll 0%; vertical-align: top; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;" bgcolor="white" height="789" width="582"><!--[endif]--><!--[if !mso]-->

<!--[endif]-->
public void run()
{
try
{
while(true)
{
//若消息堆栈为空,或者没有当前信息需要发送

boolean startBroadcast = chatFrame2.getBroadcastStart();

if (!startBroadcast)

{
try

Thread.sleep(500); // 线程睡眠500毫秒后重新检测

catch(InterruptedException ex){}

continue;

}

int lengthOfChatClients = chatClients.size(); //线程个数

for(int i=0; i < lengthOfChatClients; i++) //对每个线程进行操作

{

if(chatClients.get(i).equals("[EMPTY]")) //若是退出进程

continue;

comThread1 = (CommunicateThread)chatClients.get(i);

msgStack = comThread1.inforStack;

int lengthOfMsgStack = msgStack.size(); //对消息堆栈进行广播

for(int j=0; j<lengthOfMsgStack; j++)

{

string = (String)msgStack.get(j);

broadcastInfor = string;

broadcast("[MESSAGE]" + broadcastInfor);

boolean temp = msgStack.removeElement(string);

}
}
try
{

chatFrame2.stopBroadcast(); //停止广播

Thread.sleep(1000);

}

catch(InterruptedException ex) {}

}
}

catch(Exception e){}

}
<!--[if !mso]-->
<!--[endif]--><!--[if !mso & !vml]--><!--[endif]--><!--[if !vml]--> <!--[endif]-->


广播线程,主要是和接受消息线程达到同步,通过共享内存堆栈来实现数据的交互。然后广播线程需要解决的问题就是如何实现线程的数据广播,这是使用的是,调用创建的线程,然后调用CommunicateThread的通信进行数据的广播。其中名字的广播也是同样的一个道理。
执行效果演示
演示效果如下图所示(1-8)

<!--[if gte vml 1]> <![endif]--><!--[if !vml]--><!--[endif]-->

图1-8是windows下的运行效果,充分说明在Linux下开发的应用程序的可移植性,在Windows下运行无阻

<!--[if !supportLists]-->Ø <!--[endif]-->程序可以实现公聊和私聊<!--[if !supportFootnotes]-->[10]<!--[endif]-->,公聊在服务器端将加入聊天记录,私聊则只是发给指定用户,服务器端不保留聊天信息。

<!--[if !supportLists]-->Ø <!--[endif]-->收到系统消息,和用户变化都会有声音提示。

<!--[if !supportLists]-->Ø <!--[endif]-->完全可以单机来调试信息,也试过在Linux下运行服务器端,在Windows下使用客户端进行访问,访问方式没有区别,通信也没有故障。

<!--[if !supportLists]-->Ø <!--[endif]-->当服务器退出时,或者说用户端失去服务器连接时,用户将需要重新连接,当然也可以实现超时退出的方式,这样可以实现重新连接。

<!--[if !supportLists]-->Ø <!--[endif]-->可扩展功能:系统可以选择需要发送的系统消息的对象,这样可以使系统消息发送更加灵活。

<!--[if !supportLists]-->Ø <!--[endif]-->用户可以通过右边的list得到当前的在线用户的状况

<!--[if !supportLists]-->Ø <!--[endif]-->用户可以通过左边的textArea得到当前群中用户所发送的消息的记录<!--[if !supportFootnotes]-->[11]<!--[endif]-->

<!--[if !supportLists]-->Ø <!--[endif]-->当用户连接失败,可以选择重新登陆,重新登陆就不需要重新输入用户名。

<!--[if !supportLists]-->Ø <!--[endif]-->假如用户登陆时,没有指定连接地址,将默认为localhost地址<!--[if !supportFootnotes]-->[12]<!--[endif]-->

<!--[if !supportLists]-->Ø <!--[endif]-->用户可以通过直接按Enter键发送消息<!--[if !supportFootnotes]-->[13]<!--[endif]-->

总结
经过一个星期的编码,基本完成了课题任务。从中也学到了不少的东西,锻炼了自己的独立开发能力。其中,对Java语言也有了一定的了解,也被Java语言的强大类库所折服,以及Java环境提供的规范语言所欣喜。正因为有这样优秀的语言,和优秀的类库使得这次的任务能顺利的完成。
从中让我深有体会的是,Java的多线程编程。让我真正有机会接触多线程的编程,而Java语言的强大也使得这样的一个过程,不是非常的艰难。Java多线程编程,一般采用继承Thread类或者采用Runnable接口来实现。
Windows平台和Linux平台对于Java语言,不同的只是虚拟机,对于程序,对于编码没有区别,这也是能让我顺利完成Linux平台的应用程序的一个保证。
通过书写这篇文档,我也从中琢磨了许多的东西,如RoseUML等面向对象实现概念,通过尝试也学习了其中工具带来的方便。
分享到:
评论

相关推荐

    网络聊天室java实现

    【网络聊天室java实现】是基于Java编程语言的网络通信项目,主要运用了Java的Socket编程来构建一个多人在线实时交流的平台。在Java中,Socket是网络通信的基础,它为两台机器间的应用程序提供了双向通信的通道。通过...

    网络聊天室java ,C/S

    【标题】:“网络聊天室java,C/S” 【描述】:“这是一个基于Java技术实现的聊天室项目,采用了经典的客户端/服务器(C/S)架构。在这个项目中,我们将深入理解Java网络编程,以及如何构建一个实时通信的应用。” ...

    java网络聊天室源码

    Java网络聊天室源码是一个基于Swing图形用户界面(GUI)和TCP协议实现的多线程通信项目。这个源码提供了学习Java网络编程和多线程处理的实用示例。让我们深入探讨一下其中涉及的关键技术点。 首先,TCP(传输控制...

    网络聊天室的java简单实现

    本项目以"网络聊天室的java简单实现"为主题,旨在帮助初学者理解和掌握相关技术。以下将详细介绍这个项目的知识点。 首先,我们要理解的是Java语言基础。Java是一种广泛使用的面向对象的编程语言,具有跨平台性、...

    网络聊天室 java源代码 纯代码言简意赅 一目了然

    在本项目中,我们关注的是一个基于Java实现的网络聊天室。这个聊天室的源代码设计得简洁明了,非常适合初学者学习和理解网络通信的基本原理。Java是一种广泛用于开发跨平台应用程序的语言,尤其在网络编程领域,其...

    网络聊天室

    【网络聊天室】是一个使用Java编程语言开发的实时通信应用,允许多用户通过网络进行交互式的文本聊天。这个项目展示了网络编程、多线程以及并发处理等核心IT知识点。 首先,我们要理解网络聊天室的基本工作原理。在...

    JAVA简单网络聊天室源码.rar

    【JAVA简单网络聊天室源码】是一个非常适合初学者理解JAVA网络通信技术的实践项目。这个实验报告旨在帮助学习者深入理解如何使用JAVA实现一个基本的多用户聊天平台。在这个项目中,开发者将学习到如何利用JAVA的...

    java--计算机网络课程设计-聊天室

    这是课程设计上做的聊天室。里面有很详细的代码描述。 基于java的服务器和客户端程序。

    基于Socket的Java聊天室程序

    Java聊天室程序是一种基于Socket通信技术的多用户交互平台,它允许多个客户端通过网络与服务器进行实时通信。在这个"基于Socket的Java聊天室程序"中,我们主要关注以下几个核心知识点: 1. **Socket编程**:Socket...

    Chatroom-java.rar_java 聊天_java 聊天室_网络聊天 java_聊天室 java_聊天室 java

    《Java实现的网络聊天室详解》 在信息技术领域,网络通信是不可或缺的一部分,而聊天室则是网络交流的重要载体。本文将深入探讨一个基于Java编写的网络聊天室程序,旨在帮助初学者理解网络聊天室的基本原理及其Java...

    基于java编写的网上聊天室

    【基于Java编写的网上聊天室】是一个利用Java编程语言实现的在线实时通信系统,它允许用户通过网络进行文字、语音甚至视频的交流。这个项目旨在提供一个基础的平台,让用户可以进行多对多的实时互动,是学习网络编程...

    java做的网络聊天室源码

    【标题】:“java做的网络聊天室源码”指的是使用Java编程语言实现的一个网络聊天室的完整源代码。这个项目可能是为了教学目的或者实际应用而设计的,它展示了如何利用Java的技术来创建一个可以让用户实时交流的平台...

    Java网络聊天室

    Java网络聊天室简介:程序能够连接他人主机(局域网内)、能够进行用户的注册、登录、用户密码的修改;同时能够给对方发送文字内容,并且能够显示聊天时间;局域网内聊天不能超过50个连接(因为服务器套接字...

    基于java聊天室(gui)

    总的来说,“基于Java聊天室(GUI)”项目涵盖了网络编程、GUI设计、多线程以及数据交换等多个重要知识点,对于学习和提升Java开发技能非常有价值。开发者不仅可以掌握基础的TCP/IP通信,还能深入理解如何将这些概念...

    网络聊天室(Java)

    【网络聊天室(Java)】项目是一个利用Java编程语言实现的聊天应用程序,它采用了传统的C/S(客户端/服务器)架构。在这个系统中,客户端作为用户交互的界面,负责接收用户的输入并发送到服务器,同时接收服务器返回...

    java RMI技术实现的网络聊天室

    在本案例中,“java RMI技术实现的网络聊天室”是一个使用RMI构建的多用户交互系统,可以让用户通过网络进行实时通信。以下是对这个项目的关键知识点的详细解释: 1. **RMI概念**:RMI是一种基于对象的远程调用机制...

    java网络聊天室

    【Java网络聊天室】是一个基于JDK 6.0版本开发的在线交流平台,它集成了登录、注册以及在线好友查询等一系列功能。这个项目的核心在于利用Java编程语言实现客户端与服务器之间的实时通信,使得用户能够进行实时的...

    简单聊天室java代码

    【标题】"简单聊天室java代码"所涉及的知识点主要集中在Java编程语言和网络编程领域,尤其是构建基于Java的实时通信应用。这个项目可能是一个基本的多用户交互平台,允许用户通过网络发送和接收文本消息。 【描述】...

    java简单聊天室

    该程序是一个简单的聊天程序,主要功能是可以...所以通过JAVA程序的制作来完成以前未完成的任务。本程序只是一个简单的实现通信的程序,由于时间原因没有制作更多的功能,功能的扩展可以在以后的时间里进行升级添加。

    JAVA 网络聊天室代码

    【Java 网络聊天室代码】是一个基于Socket编程实现的网络通信项目,它包括了服务器端(Server)和客户端(Client)两部分。在Java中,Socket是用于在网络中进行双向通信的基础类,它允许两个应用之间建立连接并交换...

Global site tag (gtag.js) - Google Analytics