基于Java的网络通信的设计与实现
作者:邹吕新 钟仲芳
来源:《电脑知识与技术》2010年第08
        摘要:该文采用Java平台,利用多线程机制,采用Socket进行通信,实现服务器与多客户端之间的实时交互通信。经过测试,系统运行正确,功能较完善。
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 betwee
n 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最大限度地利用了网络,JavaApplet小应用程序可在网络上传输而不受CPU和环境的限制。利用Java提供的网络功能实现网络通信,通过Socket在客户端与服务器之间建立一个连接通道进行数据的传输与通信;通过Java的多线程处理,实现多客户端的通信。
        Java平台与大多数其他平台的不同,它是一种运行在其他硬件平台上的纯软件平台。Java平台有两个组件:1)Java虚拟机(Java VM);2)Java应用程序编程接口(Java API)
        由于Java是一种与平台无关的编程语言,所以在Java平台上运行的程序,Java API和虚拟机将程序与硬件隔离开。
        1 JavaSocket通信
        Socket被称为套接字,用于描述网络的IP地址和端口,Java应用程序通过套接字向网络发出请求,或者应答网络请求来建立相互间的通信。应用Socket建立的通信相当于在两台机器之间建立了一条通信管道,两台机器之间通过该管道进行双向的通信,同时该管道也保证了通信数据的完整性。
        对于一个网络通信程序,需要编写服务端和客户端两个程序,才能实现相互通信。在Java,服务端使用ServerSocket,客户端使用Socket,它们都在Java包中定义[2]
        1.1 Socket
        Socket编程的客户端使用Socket,在通信双方需要通信时创建Socket对象,Socket类提供了8种构造方法,较常用的有两种Socket(InetAddress address,int port)Socket(String host,int port),其中InetAddressJava中定义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 线程之间的问题解决
        当多个线程与对象交互时,则需要适当的控制,以确保线程间不会产生不利的影响。因而,要编写真正的线程安全、在所有时间均能正常工作的应用程序。可用关键字synchronizedvolatile来控制对象和变量的并发访问。
        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()返回前,线程与其他线程竞争重新获得锁。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。