3.2 接收和关闭与客户的连接
ServerSocket的accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回。如果队列中没有连接请求,accept()方法就会一直等待,直到接收到了连接请求才返回。
接下来,服务器从Socket对象中获得输入流和输出流,就能与客户交换数据。当服务器正在进行发送数据的操作时,如果客户端断开了连接,那么服务器端会抛出一个IOException的子类SocketException异常:
java.net.SocketException: Connection reset by peer
|
这只是服务器与单个客户通信中出现的异常,这种异常应该被捕获,使得服务器能继续与其他客户通信。
以下程序显示了单线程服务器采用的通信流程:
public void service() { while (true) { Socket socket=null; try { socket = serverSocket.accept(); //从连接请求队列中取出一个连接 System.out.println("New connection accepted " + socket.getInetAddress() + ":" +socket.getPort()); //接收和发送数据 … }catch (IOException e) { //这只是与单个客户通信时遇到的异常,可能是由于客户端过早断开连接引起的 //这种异常不应该中断整个while循环 e.printStackTrace(); }finally { try{ if(socket!=null)socket.close(); //与一个客户通信结束后,要关闭
Socket }catch (IOException e) {e.printStackTrace();} } } }
|
与单个客户通信的代码放在一个try代码块中,如果遇到异常,该异常被catch代码块捕获。try代码块后面还有一个finally代码块,它保证不管与客户通信正常结束还是异常结束,最后都会关闭Socket,断开与这个客户的连接。
3.3 关闭ServerSocket
ServerSocket的close()方法使服务器释放占用的端口,并且断开与所有客户的连接。当一个服务器程序运行结束时,即使没有执行 ServerSocket的close()方法,操作系统也会释放这个服务器占用的端口。因此,服务器程序并不一定要在结束之前执行 ServerSocket的close()方法。
在某些情况下,如果希望及时释放服务器的端口,以便让其他程序能占用该端口,则可以显式调用ServerSocket的close()方法。例如, 以下代码用于扫描1~65535之间的端口号。如果ServerSocket成功创建,意味着该端口未被其他服务器进程绑定,否者说明该端口已经被其他进 程占用:
for(int port=1;port<=65535;port++){ try{ ServerSocket serverSocket=new ServerSocket(port); serverSocket.close(); //及时关闭ServerSocket }catch(IOException e){ System.out.println("端口"+port+" 已经被其他服务器进程占用"); } }
|
以上程序代码创建了一个ServerSocket对象后,就马上关闭它,以便及时释放它占用的端口,从而避免程序临时占用系统的大多数端口。
ServerSocket的isClosed()方法判断ServerSocket是否关闭,只有执行了ServerSocket的close() 方法,isClosed()方法才返回true;否则,即使ServerSocket还没有和特定端口绑定,isClosed()方法也会返回 false。
ServerSocket的isBound()方法判断ServerSocket是否已经与一个端口绑定,只要ServerSocket已经与一个端口绑定,即使它已经被关闭,isBound()方法也会返回true。
如果需要确定一个ServerSocket已经与特定端口绑定,并且还没有被关闭,则可以采用以下方式:
boolean isOpen=serverSocket.isBound() && !serverSocket.isClosed();
|
3.4 获取ServerSocket的信息
ServerSocket的以下两个get方法可分别获得服务器绑定的IP地址,以及绑定的端口:
◆public InetAddress getInetAddress()
◆public int getLocalPort()
前面已经讲到,在构造ServerSocket时,如果把端口设为0,那么将由操作系统为服务器分配一个端口(称为匿名端口),程序只要调用 getLocalPort()方法就能获知这个端口号。如例程3-3所示的RandomPort创建了一个ServerSocket,它使用的就是匿名端 口。
例程3-4 TimeoutTester.java
import java.io.*; import java.net.*;
public class TimeoutTester{ public static void main(String args[])throws IOException{ ServerSocket serverSocket=new ServerSocket(8000); serverSocket.setSoTimeout(6000); //等待客户连接的时间不超过6秒 Socket socket=serverSocket.accept(); socket.close(); System.out.println("服务器关闭"); } }
|
运行以上程序,过6秒钟后,程序会从serverSocket.accept()方法中抛出Socket- TimeoutException:
C:\chapter03\classes>java TimeoutTester Exception in thread "main" java.net.SocketTimeoutException: Accept timed out at java.net.PlainSocketImpl.socketAccept(Native Method) at java.net.PlainSocketImpl.accept(Unknown Source) at java.net.ServerSocket.implAccept(Unknown Source) at java.net.ServerSocket.accept(Unknown Source) at TimeoutTester.main(TimeoutTester.java:8)
|
如果把程序中的“serverSocket.setSoTimeout(6000)”注释掉,那么serverSocket. accept()方法永远不会超时,它会一直等待下去,直到接收到了客户的连接,才会从accept()方法返回。
Tips:服务器执行serverSocket.accept()方法时,等待客户连接的过程也称为阻塞。本书第4章的4.1节(线程阻塞的概念)详细介绍了阻塞的概念。
3.5.2 SO_REUSEADDR选项
◆设置该选项:public void setResuseAddress(boolean on) throws SocketException
◆读取该选项:public boolean getResuseAddress() throws SocketException
这个选项与Socket的SO_REUSEADDR选项相同,用于决定如果网络上仍然有数据向旧的ServerSocket传输数据,是否允许新的 ServerSocket绑定到与旧的ServerSocket同样的端口上。SO_REUSEADDR选项的默认值与操作系统有关,在某些操作系统中, 允许重用端口,而在某些操作系统中不允许重用端口。
当ServerSocket关闭时,如果网络上还有发送到这个ServerSocket的数据,这个ServerSocket不会立刻释放本地端口,而是会等待一段时间,确保接收到了网络上发送过来的延迟数据,然后再释放端口。
许多服务器程序都使用固定的端口。当服务器程序关闭后,有可能它的端口还会被占用一段时间,如果此时立刻在同一个主机上重启服务器程序,由于端口已经被占用,使得服务器程序无法绑定到该端口,服务器启动失败,并抛出BindException:
Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind
|
为了确保一个进程关闭了ServerSocket后,即使操作系统还没释放端口,同一个主机上的其他进程还可以立刻重用该端口,可以调用ServerSocket的setResuse- Address(true)方法:
if(!serverSocket.getResuseAddress())serverSocket.setResuseAddress(true);
|
值得注意的是,serverSocket.setResuseAddress(true)方法必须在ServerSocket还没有绑定到一个本地 端口之前调用,否则执行serverSocket.setResuseAddress(true)方法无效。此外,两个共用同一个端口的进程必须都调用 serverSocket.setResuseAddress(true)方法,才能使得一个进程关闭ServerSocket后,另一个进程的 ServerSocket还能够立刻重用相同端口。
3.5.3 SO_RCVBUF选项
◆设置该选项:public void setReceiveBufferSize(int size) throws SocketException
◆读取该选项:public int getReceiveBufferSize() throws SocketException
SO_RCVBUF表示服务器端的用于接收数据的缓冲区的大小,以字节为单位。一般说来,传输大的连续的数据块(基于HTTP或FTP协议的数据传 输)可以使用较大的缓冲区,这可以减少传输数据的次数,从而提高传输数据的效率。而对于交互式的通信(Telnet和网络游戏),则应该采用小的缓冲区, 确保能及时把小批量的数据发送给对方。
SO_RCVBUF的默认值与操作系统有关。例如,在Windows 2000中运行以下代码时,显示SO_RCVBUF的默认值为8192:
ServerSocket serverSocket=new ServerSocket(8000); System.out.println(serverSocket.getReceiveBufferSize()); //打印8192
|
无论在ServerSocket绑定到特定端口之前或之后,调用setReceiveBufferSize()方法都有效。例外情况下是如果要设置 大于64K的缓冲区,则必须在ServerSocket绑定到特定端口之前进行设置才有效。例如,以下代码把缓冲区设为128K:
ServerSocket serverSocket=new ServerSocket(); int size=serverSocket.getReceiveBufferSize(); if(size<131072) serverSocket.setReceiveBufferSize(131072); //把缓冲区的大小设为128K serverSocket.bind(new InetSocketAddress(8000)); //与8 000端口绑定
|
执行serverSocket.setReceiveBufferSize()方法,相当于对所有由serverSocket.accept()方法返回的Socket设置接收数据的缓冲区的大小。
3.5.4 设定连接时间、延迟和带宽的相对重要性
◆public void setPerformancePreferences(int connectionTime,int latency,int bandwidth)
该方法的作用与Socket的setPerformancePreferences()方法的作用相同,用于设定连接时间、延迟和带宽的相对重要性,参见本书第2章的2.5.10节(设定连接时间、延迟和带宽的相对重要性)。
分享到:
相关推荐
Java网络编程的核心在于客户端与服务器端的交互,而ServerSocket是Java中用于服务器端的类,它使得服务器能够监听特定端口,接收客户端的连接请求。本文将详细讲解ServerSocket的使用方法及其在多线程环境下的应用。...
《Java网络编程精解》是孙卫琴老师的一本经典教程,主要涵盖了Java语言在网络编程领域的深度解析。这本书深入浅出地介绍了如何使用Java进行网络通信,包括基本的TCP/IP协议、套接字编程、多线程技术以及HTTP、FTP等...
Java网络编程精解PPT课件.ppt 本资源摘要信息是关于Java网络编程的PPT课件,主要介绍了基于UDP的数据报和套接字的相关知识点。 UDP协议简介 UDP(User Datagram Protocol,用户数据报协议)是一种传输层协议,...
在深入探讨Java网络编程中的非阻塞与阻塞编程之前,我们先来了解这两个概念的基本含义。阻塞编程,通常指的是在程序执行过程中,当某一部分代码遇到I/O操作时,如读写文件或网络通信,整个程序会暂停运行,等待I/O...
Java编程是信息技术领域中最受欢迎的编程语言之一,尤其在企业级应用开发中占据主导地位。"Java编程案例精解源代码"提供了丰富的实例,帮助学习者深入理解和掌握Java编程技术。这个压缩包文件包含了与书本配套的完整...
读书笔记:JAVA网络编程精解
读书笔记:孙卫琴《Java网络编程精解》源码
Java编程是计算机科学领域中最广泛使用的编程语言之一,尤其在企业级应用开发中占据着核心地位。本资源“Java编程案例精解”旨在通过实际案例深入解析Java编程的各个方面,帮助学习者掌握Java编程的核心技术和实战...
本资源“Java编程案例精解素材.rar”包含了一系列实用的Java编程示例,旨在帮助学习者深入理解和掌握Java的核心概念与技术。 1. **使用邮件客户端工具**:JavaMail API是Java中用于处理电子邮件的库,它允许开发者...
1. **Java网络API**: Java提供了一系列的API用于网络编程,包括Socket、ServerSocket、DatagramSocket和DatagramPacket等类。Socket用于实现TCP连接,ServerSocket则用于创建服务器端的监听套接字,而DatagramSocket...
在《Java编程案例精解》的光盘资料中,可能包含了大量的实战项目,比如简单的命令行应用、图形用户界面(GUI)程序、网络编程案例、多线程编程、数据库连接(JDBC)操作以及I/O流的使用。这些案例能帮助读者在实践中...
JavaScript 编程精解 中文第三版 JavaScript 编程精解 中文第三版
"Java网络编程精解-孙卫琴.rar"可能是孙卫琴老师的著作,她是一位知名的Java教育专家。这本书可能详细解析了Java网络编程的各个方面,包括URL类的使用、HTTP协议的实现、NIO(非阻塞I/O)以及高级网络编程技巧。通过...
《Java网络编程精解》是孙卫琴先生的一本经典著作,主要针对Java开发者,深入讲解了Java在网络编程领域的核心知识。这本书涵盖了从基础概念到高级应用的多个方面,旨在帮助读者理解并掌握Java如何进行网络通信。以下...
文件标题是“《JavaScript编程精解》.pdf”,描述中提到了“JavaScript 编程精解 中文第一版”,而标签同样为“JavaScript 编程精解”。部分内容重复提及了访问“稀酷客”网站的链接,这可能是出版商提供的额外资源...
Java数据库编程技术精解,是Java开发者必备的技能之一,主要围绕JDBC(Java Database Connectivity)接口进行深入探讨。JDBC是Java平台中用于与各种数据库进行交互的标准API,无论你是初学者还是经验丰富的开发人员...
《JavaScript编程精解 (第一版 带书签) 中文PDF扫描版》是一本专注于JavaScript(通常简称为JS)编程语言的书籍,涵盖了ES5(ECMAScript 2016标准)的编程技术。这本书适合那些希望提升前端开发技能的读者,特别是...
Java编程精解(孙卫琴) 经典Java书籍,你值得拥有
javascript编程精解第三版中文版,来自github的翻译,epub电子书可在手机上看。