Java语言是由SUN公司开发的适合于分布式计算的新一代面向对象程序设计语言,它是一种新的概念。首先,作为一种程序设计语言,它简单,面向对象,不依赖于机器的结构,具有可移植性,健壮性,安全性,多线程并且提供了并发机制,具有很高的性能。其次,它提供了强大和独特的网络通用机制和能力,极大简化了网络通信程序的繁琐细节,可不受CPU和环境的限制,Java已成为当今网络最好的开发语言。
一、Java多线程简介
将多线程机制蕴含在语言中,是Java的一个重要特征。所谓线程,是指程序中的一个执行流。在一个进程中,可以有多个线程。这些线程在操作系统的调度下并发执行,使得每个线程都好像在独占整个系统资源。而有了多线程这个特性,JAVA可以支持多个程序并发执行。利用Java的多线程编程接口,开发人员可以方便地写出支持多线程的应用程序,有效地减少并发并行程序设计的困难,提供程序执行效率。
1.线程的创建
有两种方法可以创建线程。第一种方法是通过继承类Thread来创建线程类。子类重载其mn0方法。实现方法如下:
classThieadNameextendsThread{
publicvoidrun(){//run是整个线程类代码的
入口
…//需要以线程方式运行的代码
}}
第二种方法是建立一个具有Runnable接口的类。由于Java不支持多继承性,如果需要类似线程方式运行且继承其他的类,就必须实现Runnable接口。Runnable接口只有一个方法run()。在类中实现此接口的方法如下:
classThieadNameextendsAppletimplementsRunnable{
publicvoidrun(){
…//需要以线程方式运行的代码
}}
2.线程的调用
如果采用第一种方法,创建的线程类的调用格式如下:ThreadNametest=newThreadName();//test是线程类
ThreadName的一个实例test.start();
start()是线程类的成员函数,用于启动该线程,该线程将自动调用run()方法。
如果采用第二种方法,创建的线程类的调用格式如下:
ThreadNametest=newThreadName();
Threadth=newThiead(test);/组过Thread创建个新的线程
th.start()
3.线程的同步及通信
系统中存在多个线程时,就需要保证线程的同步及相互通信,以期协调工作,避免发生死锁。Java提供了二个标准的Object类方法wait(),notify()和notifyAll(),以及二个Thread类方法sleep()suspend()和resume(),用于中断或唤醒线程的执行。当线程调用sleep(),wait()或suspend()方法之后线程就会由可运行状态进入阻塞状态(blocked),一旦线程睡眠时间到或者是其他线程调用了notify()或resume()方法后,此线程才会由阻塞状态进入可运行状态中,然而一个线程是否最终占有CPU,取决于系统的调度策略。Java1.0在solaris版本中实现的“绿色线程”的调用策略是让一个线程持续处于执行状态直到有一个更高优先级的线程将之打断,而Windows95和WindowsNT是给每一个处于可执行状态的线程分配一个时间片,当时间片用完时系统会调用另一个线程投入运行。
二、网间网的Socket通讯机制
TCP/IP技术的核心部分是传输层(TCP和UDP协议)、网络层(P协议)和物理层(面向各种物理硬件技术),能实现这三层协议的内核可称之为tcp/ip网络操作系统。tcp/ip协议技术中的中下层协议向外提供的只是原始的编程界面,而不是直接的用户服务,用户服务要靠核外的应用程序实现。应用程序和tcp/ip核心协议关系如图2所示。
即网间网应用程序能够直接操作的是TCP/P核心协议提供的编程界面。由于网络中资源、运算能力和信息的差异,同时又由于网间网通信完全是异步的,因此,在基于TCP/P协议的网间网中,最主要的进程间相互作用模型是客户/服务器(client/server)模型。客户和服务器分别是两个应用程序(进程),客户向服务器发出服务请求,服务器作出响应。
网间网进程通信的关键是要解决进程的标识和多传输协议的标识问题以及进程间相互作用的模式。在网间网中,全局惟一的标识一个进程需要一个三兀组表示,即用半相关(half-association)来描述一个Socket:
{协议,本地地址,本地端口号}
而一个完整的网间网进程通信需要由两个进程组成(两个端进程),因此一个完整的网间网进程通信必须用一个五元组表示,即用相关(association)来描述一■个完整的Socket:{协议,本地地址,本地端口号,远地地址,远地端口号}其中,一个确定的网间网进程通信只能使用同一个内核的高层协议,不可能通信的一端用TCP而另一端用UDP协议。故而,两个协议相同的半相关才能组成一个合适的相关,两个三元组组合起来是一个五兀组而非六兀组。每一个Socket有一个本地惟一的Socket号,由操作系统分配。一个本地Socket号,完整地描述了本地进程以及与之通信的远地进程,因此Socket的语义具有网络一致性,准确地描述了网络进程。因此,Socket的关键是建立客户和服务器之间的相关。
Socket编程界面最早由4BSDUNIX系统提出,其主要目的是解决网间网进程通信(IPC)问题。故而,Socket系统调用与UNIX的文件访问有许多类似之处,是对UNIX输入输出的扩充。Socket是面向客户/服务器模型设计的,针对客户和服务器程序提供了不同的Socket系统调用。服务器拥有全局公认的半相关Socket,这就保证了任何客户都可以在网络的任何地方随机向它发出联接请求和信息请求。Socket的数据信息是原始字节流的形式,通信双方要在此基础上进行约定的数据格式化和解释等处理(即相同的协议),然后才能进行进一步的具体应用操作,这也是实现某种协议的过程。基于Socket的这种通信机制,在网关(gateway)的作用下可实现TCP/协议和其他低级协议,如现场总线协议CAN(ControllerAreaNetwork)协议的转换,进而扩展Internet应用领域,将实时控制功能带入网络。Socket通信机制提供了两种通讯方式:有联接和无联接方式,分别面向不同的应用需求。使用有联接方式时,通信链路提供了可靠的,全双工的字节流服务。在该方式下,通信双方必须创建一个联接过程并建立一条通讯链路,以后的网络通信操作完全在这一对进程之间进行,通信完毕关闭此联接过程。使用无联接方式时其系统开销比有联接方式小,但通信链路提供了不可靠的数据报服务,不能保证信源所传输的数据一定能够到达信宿。在该方式下,通信双方不必创建一个联接过程和建立一条通讯链路,网络通信操作在不同的主机和进程之间转发进行。
面向客户/服务器方式的Socket通信机制模型如图3所示。
三、Java多线程在网络编程中的应用
1.Socket客户端程序设计我们通过一个简单的应用程序实例来说明其通信程序的工作过程和编程特点。
?X'
importjava.io.;
X
importjava.net.;publicclassJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServerSockets=newServerSocket(PORT)System.out.println(“Started:”s)try
{Socketsocket=s.accept();try
{System.out.println(“Connectionaccepted:”+socket)
DatalnputStreamsi=newDataInputStream(socket.getInputStream());
PrintSteamso=newPrintStream(socket.getOutputStream());while(true){Stringstr=in.readLine();if(str.equal(“END”))break;
System.out.println(“Echoing:”+str);System.out.println(str);
}finally{
System.out.println(“closing"?”);}}}
此应用程序的功能是从输入流中获取从服务器方发来的数据,并显示在屏幕上。Socket类是覆盖在一个与平台有关的实现之上的,只是它把具体的系统细节从java程序中屏蔽了。因而在程序开始部分引入了java.net包中的所有的Socket类。基于此,Java程序即可实现与平台的无关性。网络通信的目的主要是对网络资源的访问和操作。在建立了新的Socket对象实体s后,利用outputStream(),getTnputStream()方法建立输出、输入流。这样,访问的网络资源的过程就变成了处理流对象的过程,即以数据流中的方法读写应用程序端。数据流(Stream)可以理解成数据的通信途径,在建立好应用程序和资源方的通信通道后,远方的数据就可以自动传输过来。在程序的结尾用close()方法关闭输入、输出流和Socket,这样将释放所占用的系统资源。
Java语言的Socket通信机制和UNIX系统的输入输出操作(open-read-write-close)相类似。其客户端基本操作程序的编写概括起来包括以下四步:①打开Socket,即创建一个Socket对象实体:②创建与此Socket联接的输入输出流;③根据服务器的协议向此Socket写数据或从Socket读数据:④关闭输入、输出流和Socket。
2.Socket服务器端程序设计基于Java语言的Socket服务器端基本操作过程和客户端过程相对应,其程序的编写也包括以下五步:①打开SeneSocket,即创建一个ServerSocket对象实体在指定端口为客户端请求的Socket服务;②使用ServerSocket类的accept()方法接收来自客户端的联接请求;③使用新建的Socket对象创建输入、输出流对象;④通过对流对象的操作完成客户端的处理请求,并将结果返回给客户端;⑤当客户端和服务器工作结束时,关闭输入、输出流,用ServerSocket类的close()方法关闭Sacket。
服务器通常分为并发服务器和重复服务器,并发服务器只接收客户请求,不处理请求,客户请求由它的fork之进程处理和响应;而重复服务器接收、处理并响应客户请求,即工作时只能同时和一个客户程序联接。下面给出使用Java的多线程实现并发服务器通信程序。具体地实现并发服务器的思路是:在服务器的程序中首先创建单个ServerSocke^并调用accept()来等候一个新连接,然后用accept()返回的Socket新建一个线程,它只为连接的特定的客户提供服务。接着再调用accept(),等候下一个亲新的连接请求。
(1)服务器端主线程程序的实现
?..+x.importjava.io.;
x
importjava.net.;publicclassMultiJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServeiSockets=newServerSocket(PORT);System.out.println(“ServerStarted”)try
{while(true)
{Socketsocket=s.accept()by
{newMyserverHandler(socket)
}catch(IOExceptione){socket.close()
}
}
}finally{
si.close()so.close();s.close();
}
}
(2)服务器端子线程程序的实现publicclassMyserverHandlerextendsThread{privateSocketsocket;
privateBufferReaderin;privatePrintWriterout;
publicMyserverHandler(Sockets)throwsIOException
{socket=s;
in=newBufferReader(newInputStreamReader(socket.getlnputStream()));
out=newPrintWriter(newBufferedWriter(newOutStreamWriter(socket.getOutputStream()))true);
start();
publicvoidiun(){
by{
while(true){
Stringstc=in.readLine()if(str.equals(“END”)break;
System.out.println(“Echoing:+str);out.pnintln(str)
}
System.out.Println(“closing"?,,);
}finally{
try{
socket.close();
}catch(IOException。){}
}
1
四、结语
线程是系统调度和运行的最小单位,采用多线程能够开发出具有并行性和高可靠性的通信程序,
并且可以节省系统资源。文中从原理和应用角度探讨了Java多线程机制的实现技术结合一个具体的多用户并发服务器网络通信程序,说明开发网络通信程序的一般方法,所举实例对于实际网络通信程序的开发有一定的实用价值。