| 网站首页 | 文章中心 | 电子书下载 | 矢量图库 | 视频教程 | 素材下载 | 程序代码下载 | JS代码 | 论坛 | 
常用软件类:
|杀毒安全 |联络聊天 |网络软件 |多媒体类 |系统工具 |图形图像 |系统工具 |应用软件 |行业软件
开发设计类:
|动画制作 |图像处理 |3D设计 |操作系统 |站长学院 |网络相关 |WEB设计 |数据库类 |程序开发
Java 套接字(Socket)详解

作者:未知    文章来源:网络    点击数:    更新时间:2007-6-3
 

 

总结一下客户机

我们的类研究完了。在我们继续往前讨论服务器端的情况之前,让我们回顾一下创建和使用Socket的步骤:

1.用您想连接的机器的IP地址和端口实例化Socket(如有问题则抛出Exception)。

2.获取Socket上的流以进行读写。

3.把流包装进BufferedReader/PrintWriter的实例,如果这样做能使事情更简单的话。

4.对Socket进行读写。

5.关闭打开的流。

5创建服务器Socket

创建RemoteFileServer类
importjava.io.*;
importjava.net.*;
publicclassRemoteFileServer{intlistenPort;publicRemoteFileServer(intlistenPort){this.listenPort=listenPort;}
//允许客户机连接到服务器,等待客户机请求
publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort);
SocketincomingConnection=null;
while(true){incomingConnection=server.accept();
handleConnection(incomingConnection);}}
catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}
catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}
//与客户机Socket交互以将客户机所请求的文件的内容发送到客户机
publicvoidhandleConnection(SocketincomingConnection){try{OutputStreamoutputToSocket=incomingConnection.getOutputStream();
InputStreaminputFromSocket=incomingConnection.getInputStream();
BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket));
FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));
BufferedReaderbufferedFileReader=newBufferedReader(fileReader);
PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream());
Stringline=null;while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();
streamWriter.close();
streamReader.close();}
catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);
e.printStackTrace();}}
publicstaticvoidmain(Stringargs[]){RemoteFileServerserver=newRemoteFileServer(1001);
server.acceptConnections();}}

跟客户机中一样,我们首先导入java.net的java.io。接着,我们给我们的类一个实例变量以保存端口,我们从该端口侦听进入的连接。缺省情况下,端口是1001。

我们的类有一个main()方法和两个其它方法。稍后我们将探究这些方法的细节。现在您只需知道acceptConnections()将允许客户机连接到服务器以及handleConnection()与客户机Socket交互以将您所请求的文件的内容发送到客户机。

实现main()

这里我们实现main()方法,它将创建RemoteFileServer并告诉它接受连接:服务器端的main()方法中,我们实例化一个新RemoteFileServer,它将在侦听端口(1001)上侦听进入的连接请求。然后我们调用acceptConnections()来告诉该server进行侦听。

接受连接

这里我们实现acceptConnections()方法,它将创建一个ServerSocket并等待连接请求:

publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort);
SocketincomingConnection=null;
while(true){incomingConnection=server.accept();
handleConnection(incomingConnection);}}
catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}
catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}

acceptConnections()用欲侦听的端口号来创建ServerSocket。然后我们通过调用该ServerSocket的accept()来告诉它开始侦听。accept()方法将造成阻塞直到来了一个连接请求。此时,accept()返回一个新的Socket,这个Socket绑定到服务器上一个随机指定的端口,返回的Socket被传递给handleConnection()。请注意我们在一个无限循环中处理对连接的接受。这里不支持任何关机。

无论何时如果您创建了一个无法绑定到指定端口(可能是因为别的什么控制了该端口)的ServerSocket,Java代码都将抛出一个错误。所以这里我们必须捕捉可能的BindException。就跟在客户机端上时一样,我们必须捕捉IOException,当我们试图在ServerSocket上接受连接时,它就会被抛出。请注意,您可以通过用毫秒数调用setSoTimeout()来为accept()调用设置超时,以避免实际长时间的等待。调用setSoTimeout()将使accept()经过指定占用时间后抛出IOException。

处理连接

这里我们实现handleConnection()方法,它将用连接的流来接收输入和写输出:

publicvoidhandleConnection(SocketincomingConnection){try{OutputStreamoutputToSocket=incomingConnection.getOutputStream();
InputStreaminputFromSocket=incomingConnection.getInputStream();
BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(inputFromSocket));
FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));
BufferedReaderbufferedFileReader=newBufferedReader(fileReader);
PrintWriterstreamWriter=newPrintWriter(incomingConnection.getOutputStream());
Stringline=null;
while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();
streamWriter.close();streamReader.close();}
catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);
e.printStackTrace();}}

跟在客户机中一样,我们用getOutputStream()和getInputStream()来获取与我们刚创建的Socket相关联的流。跟在客户机端一样,我们把InputStream包装进BufferedReader,把OutputStream包装进PrintWriter。在服务器端上,我们需要添加一些代码,用来读取目标文件和把内容逐行发送到客户机。这里是重要的代码:
FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));
BufferedReaderbufferedFileReader=newBufferedReader(fileReader);
Stringline=null;
while((line=bufferedFileReader.readLine())!=null){streamWriter.println(line);}

这些代码值得详细解释。让我们一点一点来看:

FileReaderfileReader=newFileReader(newFile(streamReader.readLine()));

首先,我们使用Socket的InputStream的BufferedReader。我们应该获取一条有效的文件路径,所以我们用该路径名构造一个新File。我们创建一个新FileReader来处理读文件的操作。

BufferedReaderbufferedFileReader=newBufferedReader(fileReader);

这里我们把FileReader包装进BufferedReader以使我们能够逐行地读该文件。

接着,我们调用BufferedReader的readLine()。这个调用将造成阻塞直到有字节到来。我们获取一些字节之后就把它们放到本地的line变量中,然后再写出到客户机上。完成读写操作之后,我们就关闭打开的流。

请注意我们在完成从Socket的读操作之后关闭streamWriter和streamReader。您或许会问我们为什么不在读取文件名之后立刻关闭streamReader。原因是当您这样做时,您的客户机将不会获取任何数据。如果您在关闭streamWriter之前关闭streamReader,则您可以往Socket写任何东西,但却没有任何数据能通过通道(通道被关闭了)。

总结一下服务器

在我们接着讨论另一个更实际的示例之前,让我们回顾一下创建和使用ServerSocket的步骤:

1.用一个您想让它侦听传入客户机连接的端口来实例化一个ServerSocket(如有问题则抛出Exception)。

2.调用ServerSocket的accept()以在等待连接期间造成阻塞。

3.获取位于该底层Socket的流以进行读写操作。

4.按使事情简单化的原则包装流。

5.对Socket进行读写。

6.关闭打开的流(并请记住,永远不要在关闭Writer之前关闭Reader)。

6创建多线程Socket服务器

前面的示例教给您基础知识,但并不能令您更深入。如果您到此就停止了,那么您一次只能处理一台客户机。原因是handleConnection()是一个阻塞方法。只有当它完成了对当前连接的处理时,服务器才能接受另一个客户机。在多数时候,您将需要(也有必要)一个多线程服务器。

创建MultithreadedRemoteFileServer类

importjava.io.*;
importjava.net.*;
publicclassMultithreadedRemoteFileServer{intlistenPort;
publicMultithreadedRemoteFileServer(intlistenPort){this.listenPort=listenPort;}
//允许客户机连接到服务器,等待客户机请求
publicvoidacceptConnections(){try{ServerSocketserver=newServerSocket(listenPort,5);
SocketincomingConnection=null;
while(true){incomingConnection=server.accept();
handleConnection(incomingConnection);}}
catch(BindExceptione){System.out.println("Unabletobindtoport"+listenPort);}
catch(IOExceptione){System.out.println("UnabletoinstantiateaServerSocketonport:"+listenPort);}}
//与客户机Socket交互以将客户机所请求的文件的内容发送到客户机
publicvoidhandleConnection(SocketconnectionToHandle){newThread(newConnectionHandler(connectionToHandle)).start();}
publicstaticvoidmain(Stringargs[]){MultithreadedRemoteFileServerserver=newMultithreadedRemoteFileServer(1001);
server.acceptConnections();}}

这里我们实现改动过acceptConnections()方法,它将创建一个能够处理待发请求的ServerSocket,并告诉ServerSocket接受连接。

新的server仍然需要acceptConnections(),所以这些代码实际上是一样的。突出显示的行表示一个重大的不同。对这个多线程版,我们现在可以指定客户机请求的最大数目,这些请求都能在实例化ServerSocket期间处于待发状态。如果我们没有指定客户机请求的最大数目,则我们假设使用缺省值50。

这里是它的工作机制。假设我们指定待发数(backlog值)是5并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有2?6号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝。我们将在带有连接池服务器示例中说明如何限定能同时连接的客户机数目。

处理连接:

publicvoidhandleConnection(SocketconnectionToHandle){newThread(newConnectionHandler(connectionToHandle)).start();}

我们对RemoteFileServer所做的大改动就体现在这个方法上。我们仍然在服务器接受一个连接之后调用handleConnection(),但现在我们把该Socket传递给ConnectionHandler的一个实例,它是Runnable的。我们用ConnectionHandler创建一个新Thread并启动它。ConnectionHandler的run()方法包Socket读/写和读File的代码,这些代码原来在RemoteFileServer的handleConnection()中。

创建ConnectionHandler类

importjava.io.*;
importjava.net.*;
publicclassConnectionHandlerimplementsRunnable{protectedSocketsocketToHandle;
publicConnectionHandler(SocketsocketToHandle){this.socketToHandle=socketToHandle;}
publicvoidrun(){try{PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream());
BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream()));
StringfileToRead=streamReader.readLine();
BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead));
Stringline=null;
while((line=fileReader.readLine())!=null){streamWriter.println(line);}fileReader.close();
streamWriter.close();streamReader.close();}
catch(Exceptione){System.out.println("Errorhandlingaclient:"+e);
e.printStackTrace();}}}

这个助手类相当简单。跟我们到目前为止的其它类一样,我们导入java.net和java.io。该类只有一个实例变量socketToHandle,它保存由该实例处理的Socket。

类的构造器用一个Socket实例作参数并将它赋给socketToHandle。

请注意该类实现了Runnable接口。实现这个接口的类都必须实现run()方法。这里我们实现run()方法,它将攫取我们的连接的流,用它来读写该连接,并在任务完成之后关闭它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我们把InputStream和OutputStream分别包装(用Socket的getOutputStream()和getInputStream())进BufferedReader和PrintWriter。然后我们用这些代码逐行地读目标文件:

PrintWriterstreamWriter=newPrintWriter(socketToHandle.getOutputStream());
BufferedReaderstreamReader=newBufferedReader(newInputStreamReader(socketToHandle.getInputStream()));
StringfileToRead=streamReader.readLine();BufferedReaderfileReader=newBufferedReader(newFileReader(fileToRead));
Stringline=null;
while((line=fileReader.readLine())!=null){streamWriter.println(line);}

请记住我们应该从客户机获取一条有效的文件路径,这样用该路径名构造一个新File,把它包装进FileReader以处理读文件的操作,然后把它包装进BufferedReader以让我们逐行地读该文件。我们while循环中调用BufferedReader上的readLine()直到不再有要读的行。请记注,对readLine()的调用将造成阻塞,直到有字节来到为止。我们获取一些字节之后就把它们放到本地的line变量中,然后写出到客户机上。完成读写操作之后,我们关闭打开的流。

上一页  [1] [2] [3] [4] 下一页


  • 上一篇文章:

  • 下一篇文章: 没有了
  • 相关文章