基于Java的网络通信的设计与实现

时间:2022-09-09 10:58:10

基于Java的网络通信的设计与实现

摘要:该文采用Java平台,利用多线程机制,采用Socket进行通信,实现服务器与多客户端之间的实时交互通信。经过测试,系统运行正确,功能较完善。

关键词:Java;Socket;多线程;网络通信

中图分类号:TP311文献标识码:A文章编号:1009-3044(2010)08-1858-03

The Design and Implementation of Network Communication Based on Java

ZOU LV-xin, ZHONG Zhong-fang

(South-central University for Nationalities, Wuhan 430074, China)

Abstract: This article is based on communication through the Socket and is performed by Multithreading on the Java Platform. The system has realized real-time interaction between the server and multi-clients. Function tests are used to analyze the system, and effective system performance has been shown.

Key words: Java; socket; multithreads; network Communication

Java是一个广泛使用的网络编程语言,它最大的优点就是与操作系统无关,实现了“一次编程,到处运行”的特色[1]。Java最大限度地利用了网络,Java的Applet小应用程序可在网络上传输而不受CPU和环境的限制。利用Java提供的网络功能实现网络通信,通过Socket在客户端与服务器之间建立一个连接通道进行数据的传输与通信;通过Java的多线程处理,实现多客户端的通信。

Java平台与大多数其他平台的不同,它是一种运行在其他硬件平台上的纯软件平台。Java平台有两个组件:1)Java虚拟机(Java VM);2)Java应用程序编程接口(Java API)。

由于Java是一种与平台无关的编程语言,所以在Java平台上运行的程序,Java API和虚拟机将程序与硬件隔离开。

1 Java的Socket通信

Socket被称为套接字,用于描述网络的IP地址和端口,Java应用程序通过套接字向网络发出请求,或者应答网络请求来建立相互间的通信。应用Socket建立的通信相当于在两台机器之间建立了一条通信管道,两台机器之间通过该管道进行双向的通信,同时该管道也保证了通信数据的完整性。

对于一个网络通信程序,需要编写服务端和客户端两个程序,才能实现相互通信。在Java中,服务端使用ServerSocket类,客户端使用Socket类,它们都在包中定义[2]。

1.1 Socket类

Socket编程的客户端使用Socket类,在通信双方需要通信时创建Socket对象,Socket类提供了8种构造方法,较常用的有两种Socket(InetAddress address,int port)和Socket(String host,int port),其中InetAddress为Java中定义Internet统一地地址模型的类,参数address用于指定Socket通信的指定IP地址,port为用于建立连接的端口,其数值范围应大于等于1024,host表示以字符串形式表示的主机名称。当Socket对象建立成功,可以调用Socket的方法getInputStream()和getOutputStream()来获得输入流传送来的信息与获得输出流来发送信息。通信完成后,调用close()方法来关闭套接字。

1.2 Server Socket类

Socket编程的服务器端使用Server Socket类,该类的作用是实现C/S模式的通信方式下服务器端的套接字。ServerSocket类提供了4种构造方法,较常用的是ServerSocket(int port),其中port为用于建立连接的端口。当用ServerSocket(int port)创建对象成功时就可以开始准备接受连接请求了,同样,通信完成后,调用Close()方法来关闭服务器建立的套接字。

2 Java多线程处理

多线程就是同时执行一个以上的线程,一个线程的执行不必等待另一个线程执行完才执行,所有线程都可以发生在同一时刻。Java中实现多线程的方法有两种,一是继承Java.lang包中的Thread类,二是用户自己的类实现Runnable接口。

线程类Thread是由Object类直接派生出来的,它有多种构造方法,较常用的是Thread(Runnable target)。

Runnable接口只有一个方法run(),用户定义的类必须实现这个方法。Run()方法是一个比较特殊的方法,它可以被运行系统自动识别并执行。

在Java中创建线程对象,使用实现接口Runnable的方法与创建Thread类的子类的方法没有本质差别,但由于Java不支持多继承,任何类只能继承一个类,所以当已经继承了某个类时,就无法在继承Thread类,这种情况下只能通过实现Runnable接口的方法来实现程序的多线程[3]。线程的状态有五种:初始状态、就绪状态、阻塞状态、运行状态、死亡状态。在Java中是使用调度程序来调度线程的运行的。

在Java编程语言中,当线程处于空闲状态时,该线程必须将这个情况告诉其他线程,这样其他线程才能抓住机会执行它们的run过程中的代码。这项操作通常是通过静态sleep方法来进行[4]。

2.1 线程之间的问题解决

当多个线程与对象交互时,则需要适当的控制,以确保线程间不会产生不利的影响。因而,要编写真正的线程安全、在所有时间均能正常工作的应用程序。可用关键字synchronized和volatile来控制对象和变量的并发访问。

volatile关键字用成员变量的一个修饰符,每次线程访问该变量时,强迫它从共享内存中重读变量的值。而且,当变量发生变化时,强迫线程将变化值回写到共享内存。如此,在任何特定时刻,两个不同的线程总是看到某个成员变量的同一个值。但只在必需时使用volatile,滥用volatile会导致不必要地降低应用程序的执行速度。

在方法的声明中添加修饰符synchronized,确保在某一时刻,方法内只允许有一个线程。当对象的状态临时处于不一致状态时,这对于阻止其他线程进入方法有用。当线程碰到synchronized实例方法时,就会一直阻塞到可以排它性访问对象级别的互斥锁(mutex lock)为止。互斥锁在某一时刻只能由一个线程持有。当释放该锁时,所有等待的线程均竞争排它性访问权限。只有一个线程可以竞争成功,其他线程将重新回到阻塞状态,并再次等待锁的释放。如果对象上的一个synchronized方法调用同一个对象上的另一个synchronized方法,它不会阻塞来竞争对象级别的锁,因为它已经获得了排它性访问锁的权限。

使用锁来控制数据的并发访问对于避免应用程序中的微妙竞争条件至关重要。不过,当线程需要同时持有多个锁时,就会产生死锁。因而,为避免死锁可循环以下原则有助于规避死锁:

1) 只在必要的最短时间内持有锁。考虑使用同步语句块代替整个同步方法。

2) 尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂。

3) 创建和使用一个大锁来代替若干小锁。把这个锁用于互斥,而不是用作单个对象的对象级别锁[5]。

2.2 线程间通信

通过同步,一个线程可以安全地更改另一个线程即将读取的值。但第二个线程如何知道该值已经发生了变化呢?这就有必要借助于线程间的通信了。实现线程间通信可以使用“等待/通知机制”,而等待/通知机制有好几种,如:最小规模的等待/通知、典型等待/通知、运用同步方法的等待/通知。

等待/通知机制在Java中已深深扎根。Object是所有类的超类,它有五个方法组成了等待/通知机制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的类都从Object继承而来,因此,所有的类都有这些公有方法可供使用。而且,在子类中,因为它们都声明为final,所以不可以覆盖任何一个方法。

notify():线程使用notify()方法通知那些可能等待该对象的其他线程。如果有多个线程等待该对象,则线程规划器任意挑选出其中一个来发出通知,而其他线程继续保持等待状态。如果无任何线程等待该对象,则notify()不起作用。在调用notify()之前,线程必须获得该对象的对象级锁的排斥性访问权限。

notifyAll():此方法与notify()的工作方式相同,重要的一个差异是:当调用notifyAll()时,通知所有等待该对象的线程,而不仅仅通知一个线程。它的优点是,不必关心应当通知哪一个等待线程,而是简单地全部通知。缺点是,如果实际上只有一个线程能够实际起作用,那么,通知所有线程将是一种浪费(浪费的是处理器资源)。

wait():wait()方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象级别锁的排斥性访问权限。在进入wait()后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。

wait(long):该方法用于将当前线程置入休眠状态,直到接到通知、被中断,或完了指定的超时值为止。除了拥有超时特征之外,wait(long)的行为与wait()相同。

wait(long,int):该方法与wait(long)方法类似,区别在于,在超时值上可以加上ns。在msTimeout上加上参数nanoSec,来判断线程在返回前等待通知的总时间。但这个方法很少使用[5]。

3 服务器与多客户端网络通信的设计与实现

3.1 系统设计

1) 在服务器端创建一个ServerSocket对象,并指定端口号;

2) 运行ServerSocket的accept()方法,等候客户端的连接请求;

3) 客户端建立一个Socket对象,指定计算机地址和端口号,向服务器端发出连接请求;

4) 服务器端接收到客户端的连接请求后,此时accept()方法将返回一个Socket对象,使用该Socket对象创建一个新的线程为此客户端服务,实现并发服务器机制,然后再调用accept()方法等候下一个客户的连接请求;

5) 服务器端和客户端分别建立输入输出数据流,进行数据传输;

6) 通信结束后,服务器端和客户端分别关闭相应的流和通信Socket。

3.2 系统实现

系统功能实现如图1所示。服务器和客户端分别完成如下功能。

服务器Server实现以下功能:1)启动服务,建立侦听,接受客户端连接;2)接收、分析、转发聊天信息;3)向客户端发送系统信息;4)关闭服务,并向客户端发送服务器关闭消息。

客户端Client实现以下功能:1)连接服务器,发送用户登录请求;2)向服务器发送聊天信息;3)接收服务器转发的聊天信息;4)接收服务器发送的系统信息;5)响应服务器关闭通知消息。

3.3 系统运行效果

程序运行时,先运行服务器端,再运行客户端。由于经过多线程处理,可以同时运行多个客户端并进行相互通信。系统运行效果如图2和图3所示。

运行服务器程序后会弹出如图2所示对话框,用鼠标单击“启动服务器”按扭,则提示服务器已启动。运行客户端程序后会弹出如图3所示对话框,在最底下的编辑框中输入你在通信时的呢称,单击登录按扭,可以在服务器窗口看到提示“××××上线”,如上可以继续运行客户端程序,同时在线几个的用户就可以进行通信了。从图2中可以看到,它提示了在线用户的人数,在服务器的底端编辑框中输入信息后按回车就可以把信息发送到各个客户端了。同样,在客户端的编辑框中输入信息可以发送到各个客户端和服务器。不管在服务器还是客户端都可以选择要发送信息。

4 结束语

该文介绍在Java环境下,利用多线程机制,基于Socket类的C/S模式的网络通信的实现。随着Internet的发展,对网络通信的研究具有实用价值,它将服务于各行各业。

参考文献:

[1] 陈多.Java Applet 通讯技术的实现[J].湖南工程学院学报,2005(6):1.

[2] 宋中山.JAVA程序设计[M].北京:清华大学出版社,2005:295.

[3] 飞思科技产品研发中心.Java2应用开发指南[M].北京:电子工业出版社,2004:287-292.

[4] Cay S.Horstmann,Gary Cornell.Core Java 2 Volume II Advanced Features[M].王建华,译.北京:机械工业出版社,2003:8.

[5] Paul Hyde,Java Thread Programming[M].周良忠,译.北京:人民邮电出版社,2003:105-124.

上一篇:关联规则挖掘技术在人寿保险行业中的应用 下一篇:讨论式教学法在大学计算机基础教学中的应用