用javasocket开发高并发小型服务器_第1页
用javasocket开发高并发小型服务器_第2页
用javasocket开发高并发小型服务器_第3页
用javasocket开发高并发小型服务器_第4页
用javasocket开发高并发小型服务器_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、用JavaSocket开发高并发小型效劳器JavaSocket套接字socket为两台计算机之间的通信提供了一种机制,在JamesGosling注意到Java语言之前,套接字就早已赫赫有名。该语言只是让您不必理解底层操作系统的细节就能有效地使用套接字。1 客户机/效劳器模型在饭店里,菜单上各种具有异国情调的食品映入你的眼帘,于是你要了一份pizza。几分钟后,你用力咀嚼浇着融化的乳酪和其他你喜欢的配料的热pizza。你不知道,也不想知道:侍者从那里弄来了pizza,在制作过程中加进了什么,以及配料是如何获得的。上例中包含的实体有:美味的pizza、承受你定餐的侍者、制作pizza的厨房,当然还

2、有你。你是定pizza的顾客或客户。制作pizza的过程对于你而言是被封装的。你的恳求在厨房中被处理,pizza制作完成后,由侍者端给你。你所看到的就是一个客户机/效劳器模型。客户机向效劳器发送一个恳求或命令。效劳器处理客户机的恳求。客户机和效劳器之间的通讯是客户机/效劳器模型中的一个重要组成部分,通常通过网络进展。客户机/效劳器模型是一个应用程序开发框架,该框架是为了将数据的表示与其内部的处理和存储别分开来而设计的。客户机恳求效劳,效劳器为这些恳求效劳。恳求通过网络从客户机传递到效劳器。效劳器所进展的处理对客户机而言是隐藏的。一个效劳器可以为多台客户机效劳。多台客户机访问效劳器效劳器和客户机

3、不一定是硬件组件。它们可以是工作啊同一机器或不同机器上的程序。、考虑一个航空定票系统中的数据输入程序:数据乘客名、航班号、飞行日期、目的地等可以被输入到前端客户机的应用程序中。一旦数据输入之后,客户机将数据发送到后端效劳器端。效劳器处理数据并在数据库中保存数据。客户机/效劳器模型的重要性在于所有的数据都存放在同一地点。客户机从不同的地方访问同一数据源,效劳器对所有的输入数据应用同样的检验规那么。万维网为为什么要将数据的表示与其存储、处理别分开来提供了一个很好的例子。在Web上,你无需控制最终用户用来访问你数据的平台和软件。你可以考虑编写出适用与每一种潜在的目的平台的应用程序。客户机/效劳器应用

4、程序的效劳器部分管理通过多个客户机访问效劳器的、多个用户共享的资源。说明客户机/效劳器程序的效劳器部分强大功能的最好例子应该是Web效劳器,它通过Internet将HTML页传递给不同的Web用户。Java编程语言中最根本的特点是在Java中创立的程序的代码的可移植性。因为具有其他语言所不具备的代码可移植性,Java允许用户只要编写一次应用程序,就可以在任何客户机系统上发布它,并可以让客户机系统解释该程序。这意味着:你只要写一次代码,就能使其在任何平台上运行。2 协议当你同朋友交谈时,你们遵循一些暗含的规那么或协议。例如:你们俩不能同时开始说话,或连续不连续地说话。假设你们这样作的话,谁也不能

5、理解对方所说的东西。当你说话时,你的朋友倾听,反之亦然。你们以双方都能理解的语言和速度进展对话。当计算机之间进展通讯的时候,也需要遵循一定的规那么。数据以包的形式从一台机器发送到另一台。这些规那么管理数据打包、数据传输速度和重新数据将其恢复成原始形式。这些规那么被称为网络协议。网络协议是通过网络进展通讯的系统所遵循的一系列规那么和惯例。连网软件通常实现有上下层次之分的多层协议。网络协议的例子有:TCP/IP、UDP、AppleTalk和NetBEUI。Java提供了一个丰富的、支持网络的类库,这些类使得应用程序能方便地访问网络资源。Java提供了两种通讯工具。它们是:使用用户报文协议UDP的报

6、文和使用传输控制协议/因特网协议TCP/IP的Sockets套接字。数据报包是一个字节数组从一个程序发送程序传送到另一个承受程序。由于数据报遵守UDP,不保证发出的数据包必须到达目的地。数据报并不是可信赖的。因此,仅当传送少量数据时才使用,而且发送者和承受者之间的间隔间隔不大,假设是网络交通顶峰,或承受程序正处理来自其他程序的多个恳求,就有时机出现数据报包的丧失。Sockets套接字用TCP来进展通讯。套接字模型同其他模型相比,优越性在于其不受客户恳求来自何处的影响。只要客户机遵循TCP/IP协议,效劳器就会对它的恳求提供效劳。这意味着客户机可以是任何类型的计算机。客户机不再局限为UNIX、W

7、indows、DOS或Macintosh平台,因此,网上所有遵循TCP/IP协议的计算机可以通过套接字互相通讯。3 Sockets套接字3.1 Sockets概况在客户机/效劳器应用程序中,效劳器提供象处理数据库查询或修改数据库中的数据之类的效劳。发生在客户机和效劳器之间的通讯必须是可靠的,同时数据在客户机上的次序应该和效劳器发送出来的次序一样。什么是套接字?既然我们已经知道套接字扮演的角色,那么剩下的问题是:什么是套接字?BruceEckel在他的?Java编程思想?一书中这样描绘套接字:套接字是一种软件抽象,用于表达两台机器之间的连接“终端。对于一个给定的连接,每台机器上都有一个套接字,您

8、也可以想象它们之间有一条虚拟的“电缆,“电缆的每一端都插入到套接字中。当然,机器之间的物理硬件和电缆连接都是完全未知的。抽象的全部目的是使我们无须知道不必知道的细节。简言之,一台机器上的套接字与另一台机器上的套接字交谈就创立一条通信通道。程序员可以用该通道来在两台机器之间发送数据。当您发送数据时,TCP/IP协议栈的每一层都会添加适当的报头信息来包装数据。这些报头帮助协议栈把您的数据送到目的地。好消息是Java语言通过"流"为您的代码提供数据,从而隐藏了所有这些细节,这也是为什么它们有时候被叫做流套接字streamingsocket的原因。把套接字想成两端上的听筒,我和您通

9、过专用通道在我们的听筒上讲话和聆听。直到我们决定挂断,对话才会完毕除非我们在使用蜂窝。而且我们各自的线路都占线,直到我们挂断。假设想在没有更高级机制如ORB以及CORBA、RMI、IIOP等等开销的情况下进展两台计算机之间的通信,那么套接字就适宜您。套接字的低级细节相当棘手。幸运的是,Java平台给了您一些虽然简单但却强大的更高级抽象,使您可以容易地创立和使用套接字。传输控制协议TCP提供了一条可靠的、点对点的通讯通道,客户机/效劳器应用程序可要通过TCP进展通讯,客户机和效劳器程序建立连接并绑定套接字。套接字用于处理通过网络连接的应用程序之间的通讯。客户机和效劳器之间更深化的通讯通过套接字完

10、成。Java被设计成一种连网语言。它通过将连接功能封装到套接字类里而使得网络编程更加容易。套接字类即Socket类它创立一个客户套接字和ServerSocket类它创立一个效劳器套接字。套接字类大致介绍如下:lSocket是基类,它支持TCP协议。TCP是一个可靠的流网络连接协议。Socket类提供了流输入/输出的方法,使得从套接字中读出数据和往套接字中写数据都很容易。该类对于编写因特网上的通讯程序而言是必不可少的。lServerSocket是一个因特网效劳程序用来监听客户恳求的类。ServerSocket实际上并不执行效劳;而是创立了一个Socket对象来代表客户机。通讯由创立的对象来完成。

11、3.2 IP地址和端口因特网效劳器可以被认为是一组套接字类,它们提供了一般称为效劳的附加功能。效劳的例子有:电子邮件、远程登录的Telnet、和通过网络传输文件的文件传输协议FTP。每种效劳都与一个端口相联络。端口是一个数值地址,通过它来处理效劳恳求就象恳求Web页一样。TCP协议需要两个数据项:IP地址和端口号。因此,当键入时,你是如何进入金诺的主页呢?因特网协议IP提供每一项网络设备。这些设备都带有一个称为IP地址的逻辑地址。由因特网协议提供的IP地址具有特定的形式。每个IP地址都是32位的数值,表示4个范围在0到255之间的8位数值金诺已经注册了它的名字,分配给注意:域名效劳或DNS效劳

12、是将。假设没有指明端口号,那么使用效劳文件中效劳器的端口。每种协议有一个缺省的端口号,在端口号未指明时使用该缺省端口号。端口号应用21 FTP.传输文件23 Telnet.提供远程登录25 SMTP.传递邮件信息67 BOOTP.在启动时提供配置情况80. 传输 Web 页109POP.使用户能访问远程系统中的邮箱让我们再来看一下URL:URL的第一部分意味着你正在使用超文本传输协议,该协议处理Web文档。假设没有指明文件,大多数的Web效劳器会取一个叫index.html文件。因此,IP地址和端口既可以通过明确指出URL各部分来决定,也可以由缺省值决定。4创立Socket客户我们将在本部分讨

13、论的例如将说明在Java代码中如何使用Socket和ServerSocket。客户机用Socket连接到效劳器。效劳器用ServerSocket在端口1001侦听。客户机恳求效劳器C:驱动器上的文件内容。创立RemoteFileClient类publicclassRemoteFileClientprotectedBufferedReadersocketReader;protectedPrintWritersocketWriter;protectedStringhostIp;protectedinthostPort;/构造方法publicRemoteFileClient(StringhostIp

14、,inthostPort)this.hostIp=hostIp;this.hostPort=hostPort;/向效劳器恳求文件的内容publicStringgetFile(StringfileNameToGet)StringBufferfileLines=newStringBuffer();trysocketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n")

15、;catch(IOExceptione)returnfileLines.toString();/连接到远程效劳器publicvoidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOException

16、e)/断开远程效劳器publicvoidtearDownConnection()try socketWriter.close();socketReader.close();catch(IOExceptione)publicstaticvoidmain(Stringargs)remoteFileClient.setUpConnection();StringBufferfileContents=newStringBuffer();fileContents.append(remoteFileClient.getFile("RemoteFileServer.java");/remo

17、teFileClient.tearDownConnection();首先我们导入和java.io。包为您提供您需要的套接字工具。java.io包为您提供对流进展读写的工具,这是您与TCP套接字通信的唯一途径。我们给我们的类实例变量以支持对套接字流的读写和存储我们将连接到的远程主机的详细信息。我们类的构造器有两个参数:远程主机的IP地址和端口号各一个,而且构造器将它们赋给实例变量。我们的类有一个main()方法和三个其它方法。稍后我们将探究这些方法的细节。如今您只需知道setUpConnection()将连接到远程效劳器,getFile()将向远程效劳器恳求fileNameToGet的内容以及t

18、earDownConnection()将从远程效劳器上断开。实现main()这里我们实现main()方法,它将创立RemoteFileClient并用它来获取远程文件的内容,然后打印结果。main()方法用主机的IP地址和端口号实例化一个新RemoteFileClient客户机。然后,我们告诉客户机建立一个到主机的连接。接着,我们告诉客户机获取主机上一个指定文件的内容。最后,我们告诉客户机断开它到主机的连接。我们把文件内容打印到控制台,只是为了证明一切都是按方案进展的。建立连接这里我们实现setUpConnection()方法,它将创立我们的Socket并让我们访问该套接字的流:publicv

19、oidsetUpConnection()trySocketclient=newSocket(hostIp,hostPort);socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();catch(UnknownHostExceptione)catch(IOExceptione)setUpConnection()方法用主机的IP地址和端口号创立一个Socket:Socketclient=newSoc

20、ket(hostIp,hostPort);我们把Socket的InputStream包装进BufferedReader以使我们可以读取流的行。然后,我们把Socket的OutputStream包装进PrintWriter以使我们可以发送文件恳求到效劳器:socketReader=newBufferedReader(newInputStreamReader(client.getInputStream();socketWriter=newPrintWriter(client.getOutputStream();客户机和效劳器都必须知道另一方即将发请记住我们的客户机和效劳器只是来回传送字节。送的是什

21、么以使它们可以作出适当的响应。在这个案例中,效劳器知道我们将发送一条有效的文件途径。当您实例化一个Socket时,将抛出UnknownHostException。这里我们不特别处理它,但我们打印一些信息到控制台以告诉我们发生了什么错误。同样地,当我们试图获取Socket的InputStream或OutputStream时,假设抛出了一个一般IOException,我们也打印一些信息到控制台。与主机交谈这里我们实现getFile()方法,它将告诉效劳器我们想要什么文件并在效劳器传回其内容时接收该内容。publicStringgetFile(StringfileNameToGet)StringBu

22、fferfileLines=newStringBuffer();trysocketWriter.println(fileNameToGet);socketWriter.flush();Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n");catch(IOExceptione)returnfileLines.toString();对getFile()方法的调用要求一个有效的文件途径String。它首先创立名为fileLines的StringBuffer,fileL

23、ines用于存储我们读自效劳器上的文件的每一行。StringBufferfileLines=newStringBuffer();在trycatch块中,我们用PrintWriter把恳求发送到主机,PrintWriter是我们在创立连接期间建立的。socketWriter.flush();socketWriter.println(fileNameToGet);请注意这里我们是flush()该PrintWriter,而不是关闭它。这迫使数据被发送到效劳器而不关闭Socket。一旦我们已经写到Socket,我们就希望有一些响应。我们不得不在Socket的InputStream上等待它,我们通过在w

24、hile循环中调用BufferedReader上的readLine()来到达这个目的。我们把每一个返回行附加到fileLinesStringBuffer带有一个换行符以保护行:Stringline=null;while(line=socketReader.readLine()!=null)fileLines.append(line+"n");断开连接这里我们实现tearDownConnection()方法,它将在我们使用完毕连接后负责“去除。tearDownConnection()方法只是分别关闭我们在Socket的InputStream和OutputStream上创立的B

25、ufferedReader和PrintWriter。这样做会关闭我们从Socket获取的底层流,所以我们必须捕捉可能的IOException。总结一下客户机我们的类研究完了。在我们继续往前讨论效劳器端的情况之前,让我们回忆一下创立和使用Socket的步骤:1. 用您想连接的机器的IP地址和端口实例化Socket如有问题那么抛出Exception。2. 获取Socket上的流以进展读写。3. 把流包装进BufferedReader/PrintWriter的实例,假设这样做能使事情更简单的话。4. 对Socket进展读写。5. 关闭翻开的流。5创立效劳器Socket创立RemoteFileServ

26、er类publicclassRemoteFileServerintlistenPort;publicRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允许客户机连接到效劳器,等待客户机恳求publicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(

27、incomingConnection);catch(BindExceptione)catch(IOExceptione)/与客户机Socket交互以将客户机所恳求的文件的内容发送到客户机publicvoidhandleConnection(SocketincomingConnection)tryOutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromSocket=incomingConnection.getInputStream();BufferedReaderstreamReade

28、r=newBufferedReader(newInputStreamReader(inputFromSocket);FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream();Stringline=null;while(line=bufferedF

29、ileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();publicstaticvoidmain(Stringargs)RemoteFileServerserver=newRemoteFileServer(1001);server.acceptConnections();我们的类有一个main()方法和两个其它方法。稍后我们将探究这些方法的细节。如今

30、您只需知道acceptConnections()将允许客户机连接到效劳器以及handleConnection()与客户机Socket交互以将您所恳求的文件的内容发送到客户机。实现main()这里我们实现main()方法,它将创立RemoteFileServer并告诉它承受连接:效劳器端的main()方法中,我们实例化一个新RemoteFileServer,它将在侦听端口1001上侦听进入的连接恳求。然后我们调用acceptConnections()来告诉该server进展侦听。承受连接这里我们实现acceptConnections()方法,它将创立一个ServerSocket并等待连接恳求:p

31、ublicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)acceptConnections()用欲侦听的端口号来创立ServerSocket。然后我们通过调用该ServerSocket的acce

32、pt()来告诉它开始侦听。accept()方法将造成阻塞直到来了一个连接恳求。此时,accept()返回一个新的Socket,这个Socket绑定到效劳器上一个随机指定的端口,返回的Socket被传递给handleConnection()。请注意我们在一个无限循环中处理对连接的承受。这里不支持任何关机。无论何时假设您创立了一个无法绑定到指定端口可能是因为别的什么控制了该端口的ServerSocket,Java代码都将抛出一个错误。所以这里我们必须捕捉可能的BindException就跟在客户机端上时一样,我们必须捕捉IOException,当我们试图在ServerSocket上承受连接时,它就

33、会被抛出。请注意,您可以通过用毫秒数调用setSoTimeout()来为accept()调用设置超时,以防止实际长时间的等待。调用setSoTimeout()将使accept()经过指定占用时间后抛出IOException。处理连接这里我们实现handleConnection()方法,它将用连接的流来接收输入和写输出:publicvoidhandleConnection(SocketincomingConnection)tryOutputStreamoutputToSocket=incomingConnection.getOutputStream();InputStreaminputFromS

34、ocket=incomingConnection.getInputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket);FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);PrintWriterstreamWriter=newPrintWriter(incomi

35、ngConnection.getOutputStream();Stringline=null;while(line=bufferedFileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();跟在客户机中一样,我们用getOutputStream()和getInputStream()来获取与我们刚创立的Socket相关联的流。跟在客户机端一样,我们把I

36、nputStream包装进BufferedReader,把OutputStream包装进PrintWriter。在效劳器端上,我们需要添加一些代码,用来读取目的文件和把内容逐行发送到客户机。这里是重要的代码:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();BufferedReaderbufferedFileReader=newBufferedReader(fileReader);Stringline=null;while(line=bufferedFileReader.readLine()!=null)str

37、eamWriter.println(line);这些代码值得详细解释。让我们一点一点来看:FileReaderfileReader=newFileReader(newFile(streamReader.readLine();首先,我们使用Socket的InputStream的BufferedReader。我们应该获取一条有效的文件途径,所以我们用该途径名构造一个新File。我们创立一个新FileReader来处理读文件的操作。BufferedReaderbufferedFileReader=newBufferedReader(fileReader);这里我们把FileReader包装进Buff

38、eredReader以使我们可以逐行地读该文件。接着,我们调用BufferedReader的readLine()。这个调用将造成阻塞直到有字节到来。我们获取一些字节之后就把它们放到本地的line变量中,然后再写出到客户机上。完成读写操作之后,我们就关闭翻开的流。请注意我们在完成从Socket的读操作之后关闭streamWriter和streamReader。您或许会问我们为什么不在读取文件名之后立即关闭streamReader。原因是当您这样做时,您的客户机将不会获取任何数据。假设您在关闭streamWriter之前关闭streamReader,那么您可以往Socket写任何东西,但却没有任何

39、数据能通过通道通道被关闭了。总结一下效劳器在我们接着讨论另一个更实际的例如之前,让我们回忆一下创立和使用ServerSocket的步骤:1.用一个您想让它侦听传入客户机连接的端口来实例化一个ServerSocket 如有问题那么抛出Exception。2. 调用ServerSocket的accept()以在等待连接期间造成阻塞。3. 获取位于该底层Socket的流以进展读写操作。4. 按使事情简单化的原那么包装流。5. 对Socket进展读写。6. 关闭翻开的流并请记住,永远不要在关闭Writer之前关闭Reader。6创立多线程Socket效劳器前面的例如教给您根底知识,但并不能令您更深化。

40、假设您到此就停顿了,那么您一次只能处理一台客户机。原因是handleConnection()是一个阻塞方法。只有当它完成了对当前连接的处理时,效劳器才能承受另一个客户机。在多数时候,您将需要也有必要一个多线程效劳器。创立MultithreadedRemoteFileServer类publicclassMultithreadedRemoteFileServerintlistenPort;publicMultithreadedRemoteFileServer(intlistenPort)this.listenPort=listenPort;/允许客户机连接到效劳器,等待客户机恳求publicvoi

41、dacceptConnections()tryServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingConnection);catch(BindExceptione)catch(IOExceptione)/与客户机Socket交互以将客户机所恳求的文件的内容发送到客户机publicvoidhandleConnection(Socketconnectio

42、nToHandle)newThread(newConnectionHandler(connectionToHandle).start();publicstaticvoidmain(Stringargs)MultithreadedRemoteFileServerserver=newMultithreadedRemoteFileServer(1001);server.acceptConnections();这里我们实现改动过acceptConnections()方法,它将创立一个可以处理待发恳求的ServerSocket,并告诉ServerSocket承受连接。新的server仍然需要accept

43、Connections(),所以这些代码实际上是一样的。突出显示的行表示一个重大的不同。对这个多线程版,我们如今可以指定客户机恳求的最大数目,这些恳求都能在实例化ServerSocket期间处于待发状态。假设我们没有指定客户机恳求的最大数目,那么我们假设使用缺省值50。这里是它的工作机制。假设我们指定待发数backlog值是5并且有五台客户机恳求连接到我们的效劳器。我们的效劳器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是5,所以我们一次可以放五个恳求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的效劳器仍忙于承受一号连接

44、记住队列中还有2?6号时,假设有第七个客户机提出连接申请,那么,该第七个客户机将遭到回绝。我们将在带有连接池效劳器例如中说明如何限定能同时连接的客户机数目。处理连接:publicvoidhandleConnection(SocketconnectionToHandle)newThread(newConnectionHandler(connectionToHandle).start();我们对RemoteFileServer所做的大改动就表达在这个方法上。我们仍然在效劳器承受一个连接之后调用handleConnection(),但如今我们把该Socket传递给ConnectionHandler的

45、一个实例,它是Runnable的。我们用ConnectionHandler创立一个新Thread并启动它。ConnectionHandler的run()方法包Socket读/写和读File的代码,这些代码原来在RemoteFileServer的handleConnection()中。创立ConnectionHandler类publicclassConnectionHandlerimplementsRunnableprotectedSocketsocketToHandle;publicConnectionHandler(SocketsocketToHandle)this.socketToHand

46、le=socketToHandle;publicvoidrun()tryPrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream();BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream();StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileRea

47、der(fileToRead);Stringline=null;while(line=fileReader.readLine()!=null)streamWriter.println(line);fileReader.close();streamWriter.close();streamReader.close();catch(Exceptione)e.printStackTrace();类的构造器用一个Socket实例作参数并将它赋给socketToHandle。请注意该类实现了Runnable接口。实现这个接口的类都必须实现run()方法。这里我们实现run()方法,它将攫取我们的连接的流

48、,用它来读写该连接,并在任务完成之后关闭它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我们把InputStream和OutputStream分别包装用Socket的getOutputStream()和getInputStream()进BufferedReader和PrintWriter。然后我们用这些代码逐行地读目的文件:PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream();BufferedRe

49、aderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream();StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead);Stringline=null;while(line=fileReader.readLine()!=null)streamWriter.println(line);请记住我们应该从客户机获取一条有效的文件

50、途径,这样用该途径名构造一个新File,把它包装进FileReader以处理读文件的操作,然后把它包装进BufferedReader以让我们逐行地读该文件。我们while循环中调用BufferedReader上的readLine()直到不再有要读的行。请记注,对readLine()的调用将造成阻塞,直到有字节来到为止。我们获取一些字节之后就把它们放到本地的line变量中,然后写出到客户机上。完成读写操作之后,我们关闭翻开的流。总结一下多线程效劳器让我们回忆一下创立和使用“多线程版的效劳器的步骤:1. 修改acceptConnections()以用缺省为50或任何您想要的大于1的指定数字实例化S

51、erverSocket。2. 修改ServerSocket的handleConnection()以用ConnectionHandler的一个实例生成一个新的Thread。3. 借用RemoteFileServer的handleConnection()方法的代码实现ConnectionHandler类。7创立带有连接池的Socket效劳器我们如今已经拥有的MultithreadedServer每当有客户机申请一个连接时都在一个新Thread中创立一个新ConnectionHandler。这意味着可能有一捆Thread“躺在我们周围。而且创立Thread的系统开销并不是微缺乏道的。假设性能成为了问

52、题也请不要事到临头才意识到它,更高效地处理我们的效劳器是件好事。那么,我们如何更高效地管理效劳器端呢?我们可以维护一个进入的连接池,一定数量的ConnectionHandler将为它提供效劳。这种设计能带来以下好处:?它限定了允许同时连接的数目。?我们只需启动ConnectionHandlerThread一次。幸运的是,跟在我们的多线程例如中一样,往代码中添加“池不需要来一个大改动。事实上,应用程序的客户机端根本就不受影响。在效劳器端,我们在效劳器启动时创立一定数量的ConnectionHandler,我们把进入的连接放入“池中并让ConnectionHandler打理剩下的事情。这种设计中有

53、很多我们不打算讨论的可能存在的技巧。例如,我们可以通过限定允许在“池中建立的连接的数目来回绝客户机。请注意:我们将不会再次讨论acceptConnections()。这个方法跟前面例如中的完全一样。它无限循环地调用ServerSocket上的accept()并把连接传递到handleConnection()。创立PooledRemoteFileServer类publicclassPooledRemoteFileServerprotectedintmaxConnections;protectedintlistenPort;protectedServerSocketserverSocket;pub

54、licPooledRemoteFileServer(intaListenPort,intmaxConnections)listenPort=aListenPort;this.maxConnections=maxConnections;publicvoidacceptConnections()tryServerSocketserver=newServerSocket(listenPort,5);SocketincomingConnection=null;while(true)incomingConnection=server.accept();handleConnection(incomingC

55、onnection);catch(BindExceptione)catch(IOExceptione)protectedvoidhandleConnection(SocketconnectionToHandle)PooledConnectionHcessRequest(connectionToHandle);publicvoidsetUpHandlers()for(inti=0;i<maxConnections;i+)PooledConnectionHandlercurrentHandler=newPooledConnectionHandler();newThread(currentHandler,"Handler"+i).start();publicstaticvoidmain(Stringargs)PooledRemoteFileServerserver=newPooledRemoteFileServer(1001,3);server.setUpHandle

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论