基于JavaSocket编程的网络文件服务系统研究

2022-07-08 07:19李柯
电子技术与软件工程 2022年2期
关键词:空闲服务器端线程

李柯

(西北工业大学 陕西省西安市 710000)

1 基本介绍

实际上进程之间的通信是通过向双方的套接字发送报文(应用程序交换信息分组)来实现的,进程所在的应用层通过套接字与运输层连接。根据使用的运输层协议不同,还可分为UDP套接字和TCP套接字。套接字充当一个“运输和预处理者”,检查运输层协议发来的报文并采取适当的动作。

集中式系统的部件(计算机)局限在一个地方,使用限制很多,难以实现系统的拓展、设备的移动,缺乏灵活性和便捷性,且难以应用在包括Web文档系统、学习工作中的工作流系统等当中。而对于分布式系统,系统的部件(计算机)不在同一地理位置,在物理上相互独立,地位平等,但却存在紧密的合作,用户获取资源时不需要知道资源在系统中的具体位置,分布式系统在用户面前将呈现出一个单一的系统整体。其开发、可扩展、异构、透明、安全的特点,使分布式计算系统被用于许多不同类型的应用中,如共享存储器多处理机。

2 基于TCP建立连接通讯、UDP传输数据的文件服务系统

于是我们是否能利用高级语言所提供的一些方法和代码库,通过代码的组织管理,实现利用TCP和UDP进行多线程传输数据的系统。由于Java编程语言面向对象、可移植性强(Java在自己的虚拟机JVM上编译运行,JVM上具有完整的虚拟硬件系统和完整的指令系统,使得Java程序在其他平台上只要拥有JVM就能不加修改地运行)、功能强大、操作简单,而且Java提供了多线程实现的类与接口,使利用Java实现多线程系统变得相对更加简单。

想要实现在网络中进程之间通信,就要对每一个进程赋予唯一标识。网络中的进程需要利用网络层的IP地址唯一标识网络中的主机,传输层的所用协议和端口唯一标识主机中的进程。所以,形成了三元组(IP地址,协议,端口)来标识网络的进程。进程在网络中的通信通过该三元组唯一标识其它进程并与之进行交互。

2.1 建立服务器与客户之间的TCP连接

服务器端指定一个空闲端口号创建一个ServerSocket实例,当然该端口必须是空闲端口:服务器如果指定系统或者其它进程已经占用的端口,将会返回异常,只有和空闲端口连接才能正常运行。选定空闲端口简单的方法是:1~1023已经被系统占用,只有1024~65535之间的端口号为空闲端口,可供使用。比如HTTP协议一般使用80端口,当然如要应用HTTP协议,除了使用80端口,也可以使用其他的空闲端口实现HTTP协议。还可以使用端口扫描,通过创建以主机IP地址和测试端口号为构造方法参数的socket对象,如果创建成功,便为空闲端口,如果抛出IO异常便为被占用端口。ServerSocket代表服务器端服务套接字,可以创建连接中属于服务器端的socket实例。Socket代表客户端套接字,与服务器端创建的socket实例建立连接。建立TCP连接时,两端socket将自动完成TCP需要三次握手的过程。服务端同时运行ServerSocket和与客户建立连接的服务器端Socket,而客户端只运行客户端Socket。ServerSocket以选定端口作为参数,生成与选定端口连接的serverSocket对象;Socket通过默认构造方法创建一个与随机可用端口绑定的流套接字,并通过connect方法以一个InetSocketAddress对象作为参数向服务器发送连接请求,而InetSocketAddress对象以要连接的服务器IP地址和要连接的服务器端口号作为构造方法参数。ServerSocket对象生成后负责监听来自客户的TCP连接请求,使用accept方法来监听和通过客户端进程的连接请求,并返回一个Socket类型的对象。服务器端Socket对象创建成功后,客户端与服务器就建立了一个TCP连接,并可以通过这个连接在两个套接字之间进行通信。Socket类还提供了返回输出流、输入流的方法,用于发送、接受数据。如果希望设置客户端Socket请求与服务器建立连接的等待时间,则可调用Socket的connect方法设置超时时间,当连接请求超时时,便会抛出连接超时异常。另外,ServerSocket的构造方法还提供设定接收客户端请求的队列长度的功能,当请求数量大于队列设定长度时,新的连接请求再传入服务器无法进入等待队列,客户端便会返回异常。仅当ServerSocket调用accept方法从连接队列中取出任务,新的连接请求才允许进入等待队列。但是该队列为等待队列,并不能实现多线程与服务器并发交互,在实际中应用方面并不广泛。

2.2 服务器的多线程实现

当服务器接收到第一个客户请求时,连接建立开始通信,其他客户请求连接时,这些连接请求,必须进入等待队列等待服务器响应,直到上一个客户的任务完成并结束,导致服务器无法支持多用户并发访问并与多个客户同时交互。为了实现服务器能同时与多个客户通信并且能够及时给出响应,可以设计服务器主线程负责监听和接收客户连接请求并与客户建立连接,然后为每个连接客户创建一个工作线程,这些工作线程接收主线程提供的刚由accept方法创建的服务器端socket作为参数,并由这些线程完成对应客户发送的任务。由于无限制分配线程的创建销毁开销很大而且容易造成系统内存不足,这些工作线程可以被预先创建完成并保存在线程池中,在线程池中,提前保存了一定数量这样的线程,它们在空闲状态不断询问任务队列是否有新的等待任务,取出任务队列中的任务并执行,当一个线程执行完对应的任务时,就会继续询问任务队列是否有新的等待任务并读取任务队列中的下一个任务。线程池实现了将工作线程回收利用,使每个线程池中的线程能够重新完成新的任务,使程序员可以根据系统的CPU性能、内存大小等系统属性确定线程池中线程的数目。但是线程池的问题在于如果客户端通信异常终止,会导致线程资源的泄露,如果多次发生会使得最终线程池中无线程可用,服务器虽与客户建立连接但由于线程池中线程不可用无法响应客户请求,还有多线程程序最容易产生的并发和死锁问题等风险。例如,线程A需要同步等待线程B的执行结果,那么如果不能获得B的结果,A将可能死锁,因此应避免将相关线程加入工作队列,保证执行的线程任务尽量单一;假设工作线程执行过程中被阻塞(如由于各种原因,数据传输的终止标识丢失,导致客户端一直在读取数据阶段,无法继续输入指令),致使该线程持续处于阻塞状态。如果线程池中的所有线程都由于各种原因处于阻塞状态,线程池就无法继续处理新任务,服务器也将因此瘫痪,对此可以通过调用ServerSocket所提供的setSoTimeout方法,设置等待客户响应的连接超时时间,如果连接超时,工作线程将主动结束与客户的连接;对于线程异常终止导致线程泄露,可以使用FixedThreadPool,如果在线程正常关闭前由于异常原因致使线程异常终止,会创建一个新线程代替原线程完成接下来的任务。

2.3 服务器与客户之间的UDP传输

在Java中,UDP的特定socket的类为DatagramSocket负责接收和发送UDP数据报,其提供的receive和send方法让服务器和客户可以接收、发送数据;通过创建DatagramPacket对象表示UDP数据单元数据包。需要注意的是,DatagramPacket指定了数据内容和要发送的字节数;作为接收数据的DatagramPacket无需指定发送方地址;发送数据的DatagramPacket则必须设定数据到达的目的IP地址和端口。对于传输非文本文件,应采用字节流以避免读取格式的错误,并且所有文件的储存是都是字节的储存,所以采用字节流传输。

服务器UDP发送服务(实现get方法)Get:

3 系统实现

该系统类似ftp网络文件服务程序,由于Java为socket的编程提供了丰富发工具,该系统以Java Socket TCP和UDP为基础实现,包含服务器端和客户端;该系统使用TCP协议传输用户指令和服务器反馈,使用UDP协议传输文件;服务器端支持多用户并发访问。当控制台显示连接时,客户可在控制台输入以下指令获得服务器的对应反馈:

通过TCP连接时,服务器端需保存用户当前所在目录的信息(初始连接时为服务器指定的根目录),通过cd ..可退到上一级目录,且当前目录为根目录时,不做变动。TCP连接时,客户端可能一次接收多行数据,需循环从输入流中读取,服务器端可在每次输出结束时多输出一个空行,客户端根据空行来判断每次输入的结束。get命令的执行:可先发送给TCP连接,判断get后的参数在当前目录下是否为普通文件,若不是,回复提示信息,若是,回复客户端特定信息(如OK),以及该文件的物理路径和大小,客户端接收到OK后,紧接着读取后续的信息,再通过UDP请求该文件。UDP传输文件时,需将文件分次读取到byte[]缓存中,每次封装一个DatagramPacket,依次发送给客户端,为了确保发送的顺序,可在每次发送结束时通过TimeUnit.MICROSECONDS.sleep(1)来限制发送的速度;客户端接收时,循环接收DatagramPacket,循环次数为文件大小/缓存大小+1。

对于客户端,不论是TCP协议的socket还是UDP协议的socket,代码的结束部分都会调用socket.close()方法将客户端对应的socket关闭,以避免socket超时关闭。而Handler类作为一个工作线程类,调用了LS、Cd、Get类来完成ls、cd和get功能。由于代码在同一台计算机测试,所选IP地址固定为127.0.0.1(可更改)。传输文件的保存路径保存在EchoClient中,可手动更改。这是一个简易的网络文件查找传输系统,其通过Java实现,可以帮助了解计算机网络、分布式计算、socket编程以及Java编程的相关知识。

4 结束语

计算机网络近六十年的发展将人类的生活彻底改变,它将不同空间不同时间的计算机联系起来,对人与人之间的联系交流、科学技术的研究发展产生了巨大变革。分布式计算将多个计算机相互连接作为一个整体,在相同或不同的软硬件环境下执行相同或不同的任务,其在分散的计算机之间建立紧密的连接,充分的利用计算机资源,提升效率,降低成本,引起的技术变革和更新也将促进人类科学技术的发展。

猜你喜欢
空闲服务器端线程
恩赐
Linux环境下基于Socket的数据传输软件设计
“鸟”字谜
浅析异步通信层的架构在ASP.NET 程序中的应用
彪悍的“宠”生,不需要解释
浅谈linux多线程协作
基于Qt的安全即时通讯软件服务器端设计
基于上下文定界的Fork/Join并行性的并发程序可达性分析*
Linux线程实现技术研究
网页防篡改中分布式文件同步复制系统