java 启动子进程,Java课程设计项目实例《远程启动程序》子系统

44eabd29243a81282117e05f0d79368e.png

一、前言

《远程启动程序》子系统是监控中心系统、远程主机智能控制系统、远程桌面共享等多种应用系统的必要组成部分,本子系统基于Socket通讯技术加以实现,主要的核心技术涉及Socket通讯、多线程及线程池、Java 数据流、对象序列化和反序列化、进程之间管道(Pipe)通讯和进程控制等方面的内容。

由于本文的篇幅关系,作者暂不涉及作为课程设计文档的规范格式中所必需的“系统需求”、“系统设计”和“项目总结”等章节的内容。本文的写作重点侧重于“技术应用”和“程序实现”两方面。感兴趣的读者可以将本文嵌入到自己的课程设计系统中或者引用本文相关内容,以丰富和完善自己的课程设计、毕业设计等文档。

二、子系统的实现原理

《远程启动程序》(Remote Program Launch)子系统本身的业务逻辑也并不复杂,客户端程序和远程服务器程序通过Socket进行通讯连接,远程服务器根据客户端的请求应用进程通讯机制启动待执行的远程程序。参看如下示例图所示的通讯原理示图。

e4fc3b797787f8f04c8e32bf47271e44.png

首先,客户端程序向服务器端程序发送待执行的远程“命令”(主要包括需要启动执行服务器主机中可执行程序名或者系统命令、相关的工作参数、工作目录等属性参数),服务器端程序通过Socket监听获得该客户端程序的连接请求及相关的“命令”和“参数”。

其次,服务器端程序根据客户端程序的请求命令动态地执行对应的命令(包括CMD命令和Shell脚本)或者启动对应的可执行程序(比如Windows系统平台下的*.exe程序)。

最后,服务器再通过Socket连接将依据进程通讯获得的子进程的执行结果信息,并发送回客户端程序,客户端程序最终获得待执行的远程命令或者程序的执行结果信息。

三、启动其它程序或执行相关命令

1、Runtime类中的exec方法

在某个Java程序中调用并运行其它Java程序类中的方法时,无论对该方法的调用方式是静态调用还是动态调用,都是在当前的Java程序的进程中执行的。但对于有些大型的软件应用系统,早期的系统设计方案可能是采用一个“主进程+多个线程”的系统体系结构设计实现方案,而目前更多的是应用一个“主进程+多个子进程”的系统体系结构设计实现方案(参看如下示例图所示的主进程、子进程、线程之间的通讯原理和程序的模块化设计方案)。将整个应用系统根据业务处理的需要划分为不同的进程、每个进程又分为不同的程序模块和不同的线程等,这样的系统体系结构设计将使得软件应用系统的耦合度更低、程序更加健壮。

2a6881a8e0565f6546964f4cc6abcd7b.png

但这不可避免地会涉及到在一个主进程中根据应用的需要启动多个不同的子进程,这些子进程可以是Java程序,也可以是其它语言编程的可执行程序。新启动的程序是在不同的JVM虚拟机进程中运行的,如果有一个子进程发生异常,并不会影响到其它的子进程。因此,在应用开发中,如果需要在某个Java应用程序中启动和执行本机操作系统(如Windows操作系统)中其它的可执行程序及调用操作系统中的命令行程序、以及再启动执行其它的Java程序类等类型的应用要求,以最终达到完成特定的应用功能的目的等方面的需求时,这可以通过Runtime类中的exec方法加以实现。

但由于exec方法有多种不同形式的重载方法定义(参看如下示例图所示的Runtime 类中的exec方法的定义及功能说明),可以根据应用的需要选择其中的某一种形式的exec方法。

59755e1a9bc652dfd6cc70466c297d6b.png

2、ProcessBuilder类及其start()方法

在Java程序中如果需要启动本机中的另一个应用程序,除了可以应用Runtime.exec()方法之外,也还可以应用java.lang.ProcessBuilder类中的start()方法,该start()方法与Runtime类中的exec方法同样也返回一个java.lang.Process子类的对象实例。应用该Process子类的对象实例可以在Java程序中控制启动的本机进程(如调用Process类中的destroy方法可以销毁启动的本机进程)和获得与本机进程相关的信息。本机进程(子进程)正确地创建后,会和创建它的Java程序(主进程)分别独立运行,但可以相互通讯和交换数据。

如下示例图所示的内容为java.lang.ProcessBuilder类的定义及功能说明,ProcessBuilder类是一个final类,并有两个带参数的构造方法(ProcessBuilder(List command) 构造方法和 ProcessBuilder(String... command) 构造方法),可以通过它的构造方法来直接创建出ProcessBuilder类的对象实例。

31f15dea0370c53ec8c06e4e1279d8c2.png

ProcessBuilder类可用于创建操作系统的进程,并提供涉及启动和管理进程(也就是应用程序)的功能方法。而且ProcessBuilder类相比于Runtime类而言,提供了对启动的子进程进行控制有关的更丰富的功能方法。比如,可以设置工作目录,环境变量等信息。在本系统中作者应用ProcessBuilder类中相关的功能方法根据客户端请求的命令参数启动和执行服务器主机中相关的命令程序。如下示例图所示的内容为ProcessBuilder类中的start()方法的定义及功能说明。

3935f3b7eb6c60faabff5f11a1dc0a1c.png

由于ProcessBuilder类中的start() 方法本身并没有定义启动子进程所需的命令及参数,待执行的命令或者程序等相关的进程属性集是由ProcessBuilder类的对象实例进行管理。而start() 方法利用这些进程属性集内的相关属性创建一个新的Process类的对象实例,该对象实例代表启动的子进程。

此外,start()方法可以应用ProcessBuilder类的同一对象实例重复地进行调用,以利用相同的或相关的属性创建新的子进程。但如果在执行过程中修改了由ProcessBuilder类的对象实例所管理的属性集中的相关属性项目,将会影响到后续再通过ProcessBuilder类的对象实例的start()方法启动的子进程,但从不会影响以前启动的子进程或Java程序自身的主进程。

3、ProcessBuilder类的对象实例所管理的进程属性集

(1)命令(Command)

此处的命令是一个字符串列表集合,它表示要调用的外部程序文件及其相关的参数(如果需要参数则需要给出)。

(2)环境

是从变量到值的依赖于系统的映射。初始值是当前进程环境的一个副本(可以通过System.getenv()方法获得)。

(3)工作目录

默认值是当前进程的当前工作目录,通常根据系统属性user.dir 来命名。

(4)redirectErrorStream 属性

此属性设置为false,则意味着子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过 Process.getInputStream() 和 Process.getErrorStream()方法来访问。如果将redirectErrorStream 属性值设置为 true,标准错误将与标准输出合并。这使得关联错误消息和相应的输出变得更容易。在此情况下,合并的数据可从 Process.getInputStream() 返回的流读取,而从 Process.getErrorStream() 返回的流读取将直接到达文件尾。

4、应用ProcessBuilder类中的start()方法启动执行记事本程序

在如下示例图中给出了一个应用ProcessBuilder类中的start() 方法启动并执行Windows操作系统中的记事本程序的程序代码示例,采用ProcessBuilder(String... command) 形式的构造方法构建ProcessBuilder类的对象实例。由于在程序中给定了待打开的文本文件someOneText.txt的参数,但在作者的Windows操作系统中的磁盘中没有此文件,因此在执行中便提示是否需要创建someOneText.txt文本文件。

c34faf29b2ef6b21b3159ce25a5d9b2b.png

应用ProcessBuilder类中的start() 方法启动一个命令行程序后,并不会弹出一个CMD程序界面框的窗口(参看如下示例图所示的程序代码中对“javac -help”命令的执行结果在MyEclipse开发工具的控制台中并没有输出信息),而是在后台执行的。而如果是通过Runtime类的exec方法执行对应的命令时将会弹出一个CMD程序界面框的窗口。

145040daf14fe456ab4419954a719c3a.png

在Windows系统的命令行窗口中正常执行“javac -help”命令时将会在当前命令窗口内显示输出“javac -help”命令的结果信息(对javac.exe的使用帮助提示信息),但上面示例程序的执行结果并没有看到相关的输出信息(MyEclipse开发工具的控制台为空)。因为启动的子进程并没有自己的独立输出控制台,如何获得子进程程序在正常执行状态下的输出信息、以及子进程在出现错误时所产生的错误信息、异常抛出等信息?

这可以通过Process类中的getInputStream方法得到子进程的输出流(在子进程中的输出流,而在父进程中则就是输入流),然后将子进程中的输出流(包括通过Process.getErrorStream()方法获得的错误输出流)在主进程的控制台中输出或者在主进程中获得这些输出信息,再发送给远程的客户端程序。

四、主进程和子进程实现交互和通讯

1、主进程和子进程应用管道实现通讯

进程之间相互通信有许多的实现方法,本系统中的主进程和子进程之间应用管道(Pipe)实现通讯。因为管道(实际应该是无名管道)可用于具有亲缘关系(如父子进程之间)的进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。管道其实是Linux操作系统中很重要的一种通信方式,它是把一个程序(进程)的输出直接连接到另一个程序的输入。

正是由于管道本质上是一个固定大小的缓冲区,因此某个子进程在向管道输出数据时可能会出现管道变满的状况。当这种情况发生时,随后对管道输出数据的write()方法调用将默认地被阻塞,等待管道中的数据被读取(读空),以便在管道中腾出足够的空间供write()方法调用继续的写操作。因此,在两个进程通过管道进行通讯时,很有可能会出现一个进程由于输出到管道中的数据已经满而造成该进程将处于阻塞状态。为此,必须要及时地通过读取管道中的数据而清空管道。

管道通讯是单向的,如果进程1往管道中输出数据,那么进程2就只能输入读取管道中的数据。也就是管道之数据只能从一端流向另一端,而不能双向流动,否则会造成管道中的数据混乱;管道的生命周期是随着进程的结束而结束。

2、获得主进程和子进程各自的I/O流

由于基于管道的进程之间的通讯数据是基于字节流,因此在Java程序主进程中可以通过返回的Process类的对象实例中的getInputStream() 、getErrorStream()和 getOutputStream()等方法获得对应的InputStream输入流和OutputStream输出流,最终实现Java程序主进程和所启动的本机另一个子进程进行通讯和交换数据。

因为所启动的本机另一个子进程的所有标准I/O(即stdin、stdout 和 stderr)操作都将通过Process类中的三个获得对应流的成员方法 (getOutputStream()、getInputStream() 和 getErrorStream())重定向到主进程(Java程序)中。

主进程使用这些I/O流来提供到所启动的本机另一个进程的输入信息和获得它的输出信息,从而实现主进程和子进程之间的交互和通讯。但一定要注意主进程和子进程之间的I/O流的方向对应关系,也就是子进程的输出流对应主进程的输入流,而子进程的输入流对应主进程的输出流。参看如下示例图所示的I/O流方向的对应关系示例。

89d3d8ca3a438061ecb50bc31ccf7c98.png

因此,在主进程中如果需要获得子进程的输出信息,则首先需要子进程通过它的输出流将执行的结果信息发送出,其次则在主进程中通过自身的输入流读取子进程的输出信息。

3、在Java程序主进程中获得子进程的输出信息

如下示例图所示的程序代码示例在执行中获得启动的子进程javac.exe在“-help”的命令参数下的每行输出信息,并将这些信息在MyEclipse的控制台中逐行显示输出。当然,在Java程序主进程中也可以向子进程发送参数。因为子进程并没有自己的控制台,子进程的输入信息也可以由对应的主进程程序提供。

bbe65f096edad6967debdb3b8b821dc2.png

在上面的示例程序中将ProcessBuilder类的对象实例的redirectErrorStream属性值设置为 true,从而实现将子进程的错误信息输出与正常信息输出的两个输入流进行合并,这使得关联子进程的错误消息输出和子进程正常信息输出更加方便,不再需要分别对它们进行操作和控制。

当然,也可以采用ProcessBuilder(List command) 形式的构造方法构建ProcessBuilder类的对象实例。参看如下示例图所示的程序代码示例,示例程序执行结果与前面的示例程序的执行结果完全相同。

983064b6426e68a335f379df353a51f4.png

4、应用Process.waitfor方法暂停主进程的执行过程以等待子进程退出

在主进程和子进程进行通讯交互中,可能由于子进程在执行一个比较耗时的数据处理过程后才会产生输出信息,而此时主进程则必须要等待子进程执行完毕或者完全退出后才能完全获得子进程的全部输出结果信息,否则将会出现数据丢失或者只获得子进程的部分输出结果信息。为此,可以在主进程中通过调用Process.waitfor方法暂停主进程的执行过程以等待子进程退出,然后主进程再执行剩余的代码后才真正地结束主进程。参看上面示例图所示的程序代码示例。

但不正确地调用Process.waitfor方法时,可能会导致子进程被挂起。因为针对启动的子进程执行程序的输入输出功能操作,每个系统平台都会提供有限容量的数据缓冲区。当在主进程中如果没有及时地把缓冲区中的数据读出,且子进程的执行程序如果在短时间内有大量的数据输入到缓冲区中,而主进程由于调用了Process.waitfor方法而导致被挂起。一旦数据缓冲区被填满后,启动的子进程在执行中不能再继续输出数据,就可能会被阻塞挂起。而且此时的子进程仍然在等待主进程读取数据,而主进程又在等待子进程的执行结束,两个进程相互等待,最终将会导致出现“死锁”(DeadLock)现象。

因此,只要主进程在调用Process.waitfor方法之前,能不断地读出通讯缓冲区中的数据以及时地清空通讯缓冲区中的数据,就可以避免子进程出现死锁的现象,参看上面示例图所示的程序代码示例。

当然,也可以在调用Process.waitfor方法之前,分别应用两个线程各自读出子进程中有可能输出的正常信息和错误信息,参看如下示例图所示的程序代码示例。该示例程序的执行结果与前面示例程序的执行结果相同,但通过线程来获得子进程的输出结果,主进程只需要等待子进程执行结束。这样的程序代码在结构方面更具有模块化,代码之间的耦合度更低,适用于主进程中业务比较复杂的应用系统。

b00d2420c3794a11cded02dfe769e428.png

另外要区分Process.waitfor方法和Process.exitValue方法的不同点——Process.waitfor方法是阻塞的,而Process.exitValue方法是非阻塞的。

5、终止子进程和释放相关的I/O资源

当子进程正常执行完毕后,可以通过Process类中的destroy()方法及时地终止子进程,同时也需要将在子进程启动和执行期间所创建的I/O流对象进行释放和销毁,参看上面示例图所示的程序代码示例。

五、本子系统功能程序的实现

1、StartupDistanceServerApp服务器端主程序类

(1)创建StartupDistanceServerApp服务器端主程序类

在MyEclipse开发工具中创建出一个类名称为StartupDistanceServerApp,程序包名称为com.bluedream.distance.server,并且包含有main()方法的Java程序类,该类为在线签到子系统服务器端的主程序类。StartupDistanceServerApp程序类的创建过程参看如下示例图所示。

280205d05624feb910f62906da54959e.png

(2)编程StartupDistanceServerApp程序类中的main()方法

在main()方法中首先识别给定的服务器的IP地址是否正确,因为只有在程序中给出了正确的IP地址,在线签到子系统的服务器程序才能正常地启动;然后再测试服务器是否已经启动了,主要考虑到服务器端主程序可能会被重复地执行;如果是重复执行状态,则提示错误信息,并退出当前的执行状态。如果服务器主程序没有重复执行(也就是第1次执行),则创建出ServerSocket类对象实例,并调用listenToSomeOneClientSocket方法监听在线签到子系统客户端程序的Socket连接请求。main()方法最终的程序代码参看如下示例图所示的程序代码示例。

28f1d8c418d3d97bd03795e217ad2804.png

如果服务器程序已经正常启动了,再次执行本服务器端程序将会提示出如下示例图所示的的错误提示信息,本次执行的服务器进程将会自动地退出。

0bfc91f2546b3c3de281c74cad9e9ec5.png

(3)StartupDistanceServerApp程序类的构造方法

在StartupDistanceServerApp程序类的构造方法中首先应用ThreadPoolExecutor类构建出线程池(参看如下示例图所示的程序代码示例),并定义和设置线程池相关的工作参数;然后再创建出监听特定端口的ServerSocket对象,从而使得ServerSocket能够负责接收客户端的连接请求。

4d1ef8d05e38e0bcf4d79c8731ffe1e0.png

(4)编程其中的listenToSomeOneClientSocket ()方法

在listenToSomeOneClientSocket ()方法中应用ServerSocket类中的accept方法监听客户端程序的连接请求,并获得正在与服务器连接的某个客户端的Socket对象实例,然后再为此客户创建出一个线程,并将此线程放在线程池中进行统一的调度,在线程中实现与客户端的通讯交互。此外,在程序中还需要对相关的异常进行处理,最终的程序代码参看如下示例图所示的程序代码示例。

d9238cd043f5fa9f51f3c5fb1425ae8d.png

2、StartupDistanceServerThread线程类

(1)创建StartupDistanceServerThread服务器端线程类

在MyEclipse开发工具中创建出一个类名称为StartupDistanceServerThread,程序包名称为com.bluedream.distance.server,实现Runnable接口,并且不需要包含main()方法的Java程序类,该类为服务器端的线程程序类。StartupDistanceServerThread程序类的创建过程参看如下示例图所示。

4c4a90d87c6b1bd614e9b17e5c90fa48.png

(2)线程体run方法中的功能实现

在线程体run方法中,首先通过getRequestCommandAndArgsPOFromClient方法获得客户端的请求信息(封装在PO对象中),并识别是否正确地获得了客户端的请求信息,如果封装请求信息的实体类对象为空,则意味着客户端关闭了输出流或者客户端退出,此时也就直接退出线程体run方法。参看如下示例图所示的程序代码。

74486953c3937bef73aadf0df55d7dca.png

然后根据客户端的请求执行远程程序相关的属性参数,应用executeSomeOneCommandInServer方法启动和执行远程程序而创建出子进程,并获得子进程执行完毕返回的结果(封装在实体对象中);然后再应用sendCommandResultPOToClient方法将此结果信息再传送到客户端;最后,及时关闭I/O流及Socket连接,完成一次请求调用的过程。参看如下示例图所示的程序代码。

b0823cec51b2593f99794a64a14c02e7.png

(3)executeSomeOneCommandInServer功能方法

该方法是根据所获得的客户端请求的命令程序的属性参数,并应用ProcessBuilder类中的start()方法执行对应的命令或者远程的程序而创建出一个子进程;然后再创建ReadOutInputBufferThread线程类的对象实例,并通过该线程获得启动的子进程在执行过程中的输出信息;最后,当子进程执行完毕,则及时释放I/O流和销毁子进程对象。参看如下示例图所示的程序代码。

4858d7fb65cb98ae3c283280d9f54a2b.png

本子系统将主进程和子进程相互交互的I/O操作,由线程独立地完成,从而使得系统的程序模块设计更加松耦合,也方便后期的程序代码的维护和完善。

(4)getRequestCommandAndArgsPOFromClient方法

在该方法中首先将来自于客户端的Socket输入流包装成GZIP压缩流GZIPInputStream,然后再将GZIPInputStream输入流转换为ObjectInputStream对象输入流,以方便能够从客户端发送的序列化文件中读出封装客户端命令请求参数的ServerClientMessageInfoPO实体类的对象实例。参看如下示例图所示的程序代码。

88a906574902024a2c7f6c4730e9e37a.png

(5)sendCommandResultPOToClient方法

该方法的主要功能是将子进程的执行结果封装到ServerClientMessageInfoPO实体类的对象实例中,然后再将该实体类的对象实例通过Socket输出流发送到客户端程序中,从而可以在客户端程序中最终获得所启动的远程命令或者程序的执行结果信息。

36816edc71d680f37bb89e6f7e50a4a0.png

3、ReadOutInputBufferThread读取子进程输出数据的线程类

(1)创建ReadOutInputBufferThread读取子进程输出数据的线程类

在MyEclipse开发工具中创建出一个类名称为ReadOutInputBufferThread,程序包名称为com.bluedream.distance.server,继承Thread线程类,并且不需要包含main()方法的Java程序类,该类为读取子进程输出数据的线程类。ReadOutInputBufferThread程序类的创建过程参看如下示例图所示。

6b750a7275e35bfc5d2ead0d70c097e1.png

(2)线程体run方法中的功能实现

由于ReadOutInputBufferThread线程类的主要功能是在主进程启动了子进程后,由此线程读取子进程的输出数据,并将读出的结果信息缓存在一个名称为subProcessOutputAllLineText的静态变量中。从而在服务器端的程序中获得该静态。变量中的值,它也就是子进程的执行结果信息

b9af5f7652d615dfe9d6b04f5d3b7384.png

4、StartupDistanceServerClientApp客户端主程序类

(1)创建StartupDistanceServerClientApp客户端主程序类

在MyEclipse开发工具中创建出一个类名称为StartupDistanceServerClientApp,程序包名称为com.bluedream.distance.client,并且包含有main()方法的Java程序类,该类为客户端的主程序类。StartupDistanceServerClientApp程序类的创建过程参看如下示例图所示。

91df5574064575a6cf1eca9ac21f49d9.png

(2)StartupDistanceServerClientApp类中的main方法

在main方法中通过调用testSocketServerIsRunning方法测试服务器目前是否已经启动(参看如下示例图所示的程序代码示例),如果服务器目前还没有启动,则直接退出。如果服务器目前处于正常的工作状态,则将客户端远程启动的命令及程序的属性参数封装到List集合中,再执行sendExecuteCommandToServer方法将该属性参数集通过Socket连接发送到远程服务器程序中,由远程服务器执行此命令参数和获得对应的执行结果信息。

f41e54f6901c1fc88de19d901917f62e.png

(3)testSocketServerIsRunning方法测试服务器目前是否已经启动

本系统在检测远程服务器是否启动,没有采用socket.sendUrgentData(0xff)方法用于向服务端发送紧急数据包的方式,因为在Win7操作系统下的客户端在第18次发送数据包时会抛出SocketException类型的异常。而是采用如下示例图所示的程序代码实现对连接状态的检测方法。

b4ac1e4a996e334fabd056bf4813731d.png

当Socket服务器没有正常地启动,而客户端启动时将会提示出如下示例图所示的错误提示信息,客户端程序直接退出。

bfa8a7f8cdc2d50c36814954de49b473.png

(4)sendExecuteCommandToServer方法

在此方法中将封装在List集合对象中的客户端的远程启动的命令或者程序的属性参数再包装到ServerClientMessageInfoPO实体类的对象实例中,再通过调用requestServerExecuteClientCommand方法将此实体类的对象实例通过Socket连接发送到远程服务器端程序以方便执行其中的命令或者程序。参看如下示例图所示的程序代码示例。

a0e2a66cf351c45ee427a7df24893e53.png

(5)requestServerExecuteClientCommand方法

该方法的主要功能是将封装在ServerClientMessageInfoPO实体类的对象实例中的命令参数通过Socket连接发送到远程服务器端程序中,其中为了应用了GZIP压缩的GZIPOutputStream输出流以提高传输效率和ObjectOutputStream对象输出流实现序列化传输。参看如下示例图所示的程序代码示例。

6d26cac4ec6eeebd1782fa78733f4b50.png

在成功地发送完毕命令参数后,也相应地获得服务器端程序执行的远程命令或者程序的结果信息。最后,释放Socket连接过程中的I/O流和销毁Socket对象以释放所占用的系统内存空间。参看上面示例图所示的程序代码示例。

5、封装客户端和服务器端通讯信息的ServerClientMessageInfoPO实体类

(1)创建ServerClientMessageInfoPO实体类

在MyEclipse开发工具中创建出一个类名称为ServerClientMessageInfoPO,程序包名称为com.bluedream.distance.pobject,实现Serializable接口,并且不需要包含main()方法的Java程序类,该类为封装客户端和服务器端通讯信息的实体类。ServerClientMessageInfoPO实体类的创建过程参看如下示例图所示。

746c4e9f2ae5b41337efeb7af5d10498.png

(2)ServerClientMessageInfoPO实体类中的成员属性和方法

在该实体类中主要提供有两个属性,其一为requestCommandAndArgsList 封装客户端向远程服务器端程序请求执行的远程命令属性集,另一个则是commandExecuteResult代表远程命令执行的结果信息。在该实体类中也对应地为这两个属性提供有set/get方法,参看如下示例图所示的程序代码示例。

499a6846ac5ac0e46f52fcf0a129eef3.png

六、《远程启动程序》子系统功能实现演示

1、启动服务器程序

在MyEclipse开发工具中启动“Run As”中的“Java Application”运行器,从而可以在Java虚拟机环境中执行本示例程序,参看如下示例图所示的程序执行结果。

6200dc60eda1f83f71b8e5b3215ab311.png

2、启动客户端程序

由于客户端程序启动后及时连接Socket服务器,并向服务器请求调用客户端需要启动执行的远程命令或者程序(本示例是执行“javac -help”命令),并在控制台中显示输出服务器端返回的执行结果信息,参看如下示例图所示的程序执行结果。

b31802440669c893fbbc8988e505f42c.png

3、远程命令或者程序是由服务器程序启动和执行的

客户端程序请求执行的远程命令或者程序,对于服务器端程序而言,属于子进程。在作为主进程的服务器端程序中同样也获得了子进程的执行结果信息,并在服务器程序的控制台中显示输出,参看如下示例图所示的程序执行结果。

92e3780c72ff570b56a529445fd1a650.png

4、服务器系统支持多客户端请求,参看如下示图所示的连接结果提示

5994ef8ac2e0a85d22b9a419b327b4b7.png

Java课程设计项目实例《基于微服务的在线签到》子系统

计算机等级二级Java考试辅导:“系统和环境”单元综合复习

计算机等级二级Java考试辅导:“集合类”单元综合复习

计算机等级二级考试辅导:综合应用机试模拟题及解答(第8部分)

《Java语言程序设计》测试题及参考答案(第11部分)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值