Java网络编程之服务器套接字.pdf
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《Java网络编程之服务器套接字.pdf》由会员分享,可在线阅读,更多相关《Java网络编程之服务器套接字.pdf(38页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、369第十一章服务器套接字本章内容:ServerSocket 类一些有用的服务器前一章我们从客户机的角度讨论了套接字:打开到正在监听的服务器的套接字。但是,仅有客户机套接字是不够的,如果客户机没有和服务器对话,那么它是没用的。考虑到这一点,我们前一章所讨论的内容用于写服务器程序是远远不够的。为创建Socket,程序员需要知道想要建立连接的Internet主机。在编写服务器程序时不需要提前知道谁会与程序员联系,即使知道是谁,也无法知道他何时想与之建立联系。换句话说,服务器类似于坐在电话机旁边等待呼叫到来的接收者。它们不知道是谁、会在什么时候会打电话来,只有在电话铃声响起的时候才知道有呼叫进来,然
2、后它们拿起电话与不知在何处的人(对方响应之前)进行通话。我们无法编写单独实现Socket类的程序。当然没有任何理由用Java编写的客户程序一定是与Java服务器对话 事实上,客户程序不关心服务器程序是用什么语言编写的,也不关心它具体在哪一种操作平台上运行。但是,如果Java不支持服务器程序的编写,那将是它在功能上令人吃惊的漏洞。幸运的是,Java中不存在这样的漏洞。它提供了一个ServerSocket 类,程序员可以很方便地用它编写服务器程序。服务器套接字的基本功能类似于坐在电话机旁边等待呼叫到来。从技术上讲,ServerSocket 运行在服务器上并监听到来的 TCP 连接。每个Server
3、Socket都在服务器上的指定端口监听。当远程主机上的客户Socket试图与指定端口建立连接时,服务器被激活,判定客户程序与服务器的连接,并打开两个主机之间固有的 Socket。一旦与服务器套接字建立了连接,则服务器就可以使用固有的 Socket 对象向客户机发送数据。数据总是通过固有的套接字传递。第十一章370ServerSocket 类ServerSocket 类包含了用 Java 编写服务器程序的所有内容。它包括创建新ServerSocket对象的构造器、在指定端口监听连接的方法,以及连接建立后可以发送和接收数据时返回Socket对象的方法。此外,它还有设置不同选项以及各种各样常用的方法
4、,例如 toString()方法。服务器的基本生命期是:1.利用 ServerSocket()构造器在指定端口创建一个新的 ServerSocket;2.ServerSocket利用它的accept()方法在指定端口监听到来的连接。accept()方法一直处于阻塞状态,直到有客户机试图建立连接。这时accept()方法返回连接客户机何服务器的 Socket;3.调用getInputStream()方法、getOutputStream()方法或者两者都调用来得到与客户机通信的输入流和输出流,具体调用哪一个方法还是两者都调用与具体服务器的类型有关;4.服务器和客户机根据双方都承认的协议进行交互,直
5、到关闭连接时为止;5.服务器、客户机或两者均关闭连接;6.服务器返回步骤 2,等待下一个连接到来。如果在第4 步花费的时间很长或时间无限期,则wu-ftpd这样的传统Unix 服务器会创建一个新的进程来处理每个连接,从而使服务器在同一时刻可以为多个客户机提供服务。Java程序产生一个线程来与客户机发生作用,从而使服务器能很快准备好处理下一个连接。服务器上的线程远比子进程小。事实上,服务器所要处理的进程实在太多,这也就是为什么在没有缓存进行缓冲的条件下,典型的 Unix FTP 服务器不能处理多于 400 个连接的原因。另一方面,如果所使用的协议比较简单快速,而且允许服务器在它通过时关闭连接,则
6、对于服务器处理客户机请求来说会更加有效,客户机的请求可以很快得到处理,而不需要再产生线程。操作系统将到来的连接请求放在一个先入先出(first in first out)的队列中,每个请求都寻址到指定端口。队列的默认长度通常为50,但这根据操作系统的不同可以有所变化。有些操作系统(Solaris不是)有一个队列长度最大值限制,典型的值为5。在这些系统上,队列的最大可能长度应该是小于等于 50。在未处理的连接的数服务器套接字371目达到队列的最大长度数以后,主机就拒绝在此端口再增加连接,直到队列出现空隙为止。对于许多(尽管不是全部)客户程序来说,如果它们开始的连接请求被拒绝,它们一般都会试着继续
7、建立连接,并进行很多次。对到来的连接和队列的管理由操作系统提供的服务器程序负责,用户程序不需要考虑这些。如果用户认为等待队列的长度不足够大,有几个Socket构造器允许改变队列的长度限制。但是不能在操作系统所支持的最大队列数之外增加新的队列。构造器一共有 3 个公共的 ServerSocket 构造器:public ServerSocket(int port)throws IOException,BindExceptionpublic ServerSocket(int port,int queueLength)throws IOException,BindExceptionpublic Ser
8、verSocket(int port,int queueLength,InetAddress bindaddress)throws IOException这些构造器允许用户指定端口、用于保存到来的连接请求队列的长度以及绑定本地网络的接口。它们的基本功能完全相同,但其中有一些使用默认的队列长度值和默认的绑定地址。下面我们按顺序逐个进行研究。public ServerSocket(int port)throws IOException,BindException这个构造器在变量 port 给定的端口上创建服务器套接字。如果采用 0 作为端口号,则由系统选择可用端口。由系统选择的端口有时候也称做匿名
9、端口(anonymousport),因为我们不知道它的标号。对于服务器来说,匿名端口不是很有用,因为客户机需要提前知道它与哪个端口相连。但是,也有一些情况下(我们将在后面予以讨论)匿名端口是有用的。例如,为创建一个在HTTP服务器的80端口使用的服务器套接字,可以编写如下代码:try ServerSocket httpd=new ServerSocket(80);catch(IOException e)System.err.println(e);第十一章372如果不能在所要求的端口创建服务器套接字,则此构造器触发一个 IOException(特别地情况下,是BindException)。创建S
10、erverSocket时触发IOException通常总是意味着下面两件事情中的一件:或者是因为可能有从另外一个完全不同的程序来的服务器套接字已经占用了所要求的端口;或者是因为在没有 root(超级用户)权限的情况下试图与 Unix 上范围在 1 到 1023 之间的端口建立连接。可以利用这个构造器对前一章所讲的PortScanner程序进行改变。例11-1通过试图在本地计算机上的端口创建ServerSocket对象来检查这些端口并察看哪个端口不可用。如果使用的是Unix而又不是超级用户,则此程序仅在端口号不小于1024的端口工作。例 11-1:查找本地端口import .*;import j
11、ava.io.*;public class LocalPortScanner public static void main(String args)for(int port=1;port java LocalPortScannerThere is a server on port 135.There is a server on port 1025.There is a server on port 1026.There is a server on port 1027.There is a server on port 1028.服务器套接字373public ServerSocket(i
12、nt port,int queueLength)throws IOException,BindException这个构造器在指定的端口上创建一个ServerSocket,同时根据变量queueLength指定队列长度。如果使用的计算机有多个网络接口或多个IP地址,则它在所有网络接口和IP地址上的这个端口监听。queueLength变量设置到来连接请求队列的长度 也就是说,在主机开始拒绝连接之前,到底有多少同一时刻到来的连接请求可以被存储起来。有些操作系统有一个最大队列长度,典型的值是 5。如果想要扩展队列的最大值,则可以用最大队列长度来代替。如果传递的端口号为 0,则由系统来选择可利用的端口。
13、例如,为在端口5776上创建一个服务器套接字,同时使队列中所能存储的到来的连接请求数达到 100,可以编写下面的代码:try ServerSocket httpd=new ServerSocket(5776,100);catch(IOException e)System.err.println(e);如果不能在所要求的端口创建服务器套接字,则此构造器触发一个 IOException(特别情况下是BindException)。创建ServerSocket时触发IOException通常总是意味着下面两件事情中的一件:或者是因为可能有从另外一个完全不同的程序而来的服务器套接字已经占用了所要求的端口
14、;或者是因为在没有root(超级用户)权限的情况下试图与 Unix 上范围在 1 到 1023 之间的端口建立连接。public ServerSocket(int port,int queueLength,InetAddressbindaddress)throws IOException这个构造器仅在 Java 1.1 及后续版本中可用,它用于在指定端口上创建 Server-Socket,同时指定等待队列的长度。这个ServerSocket仅绑定在指定的本地IP地址上。这个构造器对于运行在具有多个 IP 地址(比较常见的实际情况是用在大的Web服务器上)的操作系统上的服务器程序比较有用,因为它
15、允许选择所监听的地址。也就是说,这个 ServerSocket仅监听在指定地址上到来的连接,而不监听这个主机上其他地址上到来的连接。另外两个构造器默认地与所有的本地 IP 地址绑定。第十一章374例如,metalab.unc.edu 是北加州的一个指定 SPARC 工作站,它通过 IP 地址152.2.254.81 与 Internet 相连。同样的 SPARC 工作站也叫做 www.gigabit-ethernet.org,但IP地址不同(它用的是152.2.254.82)。如果要创建一个在metalab.unc.edu的5,776端口监听的服务器套接字而此套接字不在www.gigabit-
16、ethernet.org的 5776 端口监听,应该编写的代码如下:try ServerSocket httpd=new ServerSocket(5776,10,InetAddress getByName(metalab.unc.edu );catch(IOException e)System.err.println(e);如果不能在所要求的端口创建服务器套接字,则此构造器触发一个 IOException(特别情况下是BindException)。创建ServerSocket时触发BindException通常总是意味着下面两件事情中的一件:或者是因为可能有从另外一个完全不同的程序而来的服务
17、器套接字已经占用了所要求的端口;或者是因为在没有root(超级用户)权限的情况下试图与 Unix 上范围在 1 到 1023 之间的端口建立连接。接受和关闭连接ServerSocket通常以循环的形式进行操作,从而重复地接受连接请求。每通过一次循环,就调用一次 accept()方法。这个方法返回一个代表远程客户机和本地服务器之间连接的Socket对象。与客户机的交互就通过这个Socket来完成。传输过程完成时,服务器应该调用Socket对象的close()方法并准备好处理下一个到来的连接。但是,当服务器需要关闭并且不再处理任何后面到来的连接时,则应该调用ServerSocket的close()
18、方法。public Socket accept()throws IOException当服务器建立完成并准备好接受连接时,调用ServerSocket的accept()方法。这个方法是“阻塞”的:它停止执行流并且等待直到下一个客户机连接到来。当客户机连接到来时,accept()方法返回 Socket 对象。可以用这个 Socket 对象的getInputStream()方法和getOutputStream()方法返回的流来与客户机进行通信。例如:服务器套接字375ServerSocket server=new ServerSocket(5776);While(true)Socket conne
19、ction=server.accept();OutputStream out =new OutputStreamWriter(connection.getOutputStream();Out.write(Youve connected to this server.Bye-bye now.rn);Connection.close();如果不希望程序在等待连接时停止,则可以把对 accept()方法的调用放在单独的线程中。当增加异常处理部分时,代码会变得有些令人难以理解。要注意区分ServerSocket触发的异常和Socket触发的异常。ServerSocket触发的异常应该关闭服务器并登记错
20、误信息;而 Socket触发的异常应该是关闭活动的连接。accept()触发的异常是一种中间情况,既不属于ServerSocket触发的异常,也不属于Socket触发的异常。因此需要编制try 块。最后,大多数服务器都希望确保在通信结束后关闭它所接受的所有套接字。即使使用的协议指定了由客户机负责关闭连接,客户机也不总是很好地坚持协议规定。对close()方法的调用也必须打包到捕获IOException的try块中。但是,如果在关闭套接字时捕获 IOException,则不理睬它。这意味着在服务器能够关闭套接字之前客户机已经将它关闭了。下面是一个比较实际的例子:try ServerSocket
21、server=new ServerSocket(5776);while(true)Socket connection=server.accept();try OutputStreamWriter out =new OutputStreamWriter(connection.getOutputStream();out.write(Youve connected to this server.Bye-bye now.rn);connection.close();catch(IOException e)/这可能是连接的暂时错误 /比如客户端过早中断 /因此不终止循环,输出错误 /但你可以选择将此错误
22、记入日志 finally /大多数服务器在完成后要保证套接字关闭 try if(connection!=null)connection.close();catch(IOException e)第十一章376 catch(IOException e)System.err.println(e);例11-2实现了一个符合 RFC 867的简单daytime服务器。因为这个服务器仅发送一行文本作为对连接的响应,因此它几乎可以在每一个连接请求出现时予以立即处理。更加复杂的服务器会产生一个线程来处理每个请求。在这种情况下,产生线程所要花费的时间比处理请求所花费的时间要长得多。注意:如果是在Unix上运行此
23、程序,则需要在根下运行才能保证连接到端口13。如果不能在根下运行程序,则把端口号改成大于 1024 的数,例如 1313。例 11-2:Daytime服务器import .*;import java.io.*;import java.util.Date;public class DaytimeServer public final static int DEFAULT_PORT=13;public static void main(String args)int port=DEFAULT_PORT;if(args.length 0)try port=Integer.parseInt(args0
24、);if(port=65536)System.out.println(Port must between 0 and 65535);return;catch(NumberFormatException e)/使用默认端口 try ServerSocket server=new ServerSocket(port);Socket connection=null;while(true)服务器套接字377 try connection=server.accept();OutputStreamWriter out =new OutputStreamWriter(connection.getOutput
25、Stream();Date now=new Date();out.write(now.toString()+rn);out.flush();connection.close();catch(IOException e)finally try if(connection!=null)connection.close();catch(IOException e)/结束 while /结束 try catch(IOException e)System.err.println(e);/结束 catch /结束 main/结束 DaytimeServer例11-2是非常简单而又直接的。程序的前三行导入常
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 网络 编程 服务器 套接
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内