Java网络编程从入门到精通(29):服务端Socket的选项

简介:
ServerSocket 类有以下三个选项:
1.       SO_TIMEOUT   设置accept 方法的超时时间。
2.       SO_REUSEADDR 设置服务端同一个端口是否可以多次绑定。
3.       SO_RECBUF 设置接收缓冲区的大小。
一、SO_TIMEOUT 选项
可以通过SeverSocket 类的两个方法(setSoTimeout getSoTimeout) 来设置和获得SO_TIMEOUT 选项的值,这两个方法的定义如下:
public   synchronized   void  setSoTimeout( int  timeout)  throws  SocketException
public   synchronized   int  getSoTimeout()  throws  IOException 
setSoTimeout 方法的timeout 参数表示accept 方法的超时时间,单位是毫秒。在通常情况下,ServerSocket 类的accept 方法在等待客户端请求时处于无限等待状态。如HTTP 服务器在没有用户访问网页时会一直等待用户的请求。一般不需要对服务端设置等待客户端请求超时,但在某些特殊情况下,服务端规定客户端必须在一定时间内向服务端发出请求,这时就要设置等待客户端请求超时,也就是accept 方法的超时时间。当设置客户端请求超时后,accept 方法在等待超时时间后抛出一个SocketTimeoutException 异常。下面的代码 演示了如何设置和获得SO_TIMEOUT 选项的值,超时时间通过命令行参数方式传入AcceptTimeout
package  server;

import  java.net. * ;

public   class  AcceptTimeout
{
    
public   static   void  main(String[] args)  throws  Exception
    {
        
if  (args.length  ==   0 )
            
return ;
        ServerSocket serverSocket 
=   new  ServerSocket( 1234 );
        
int  timeout  =  Integer.parseInt(args[ 0 ]);
        
        serverSocket.setSoTimeout(Integer.parseInt(args[
0 ]));
        System.out.println((timeout 
>   0 ?   " accept方法将在 "
                
+  serverSocket.getSoTimeout()  +   " 毫秒后抛出异常! "  :  " accept方法永远阻塞! " );;
        serverSocket.accept();
    }
}
执行下面的命令:
java server.AcceptTimeout  3000
运行结果:
accept方法将在3000毫秒后抛出异常!
Exception in thread 
" main "  java.net.SocketTimeoutException: Accept timed out
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:
384 )
    at java.net.ServerSocket.implAccept(ServerSocket.java:
450 )
    at java.net.ServerSocket.accept(ServerSocket.java:
421 )
    at chapter5.AcceptTimeout.main(AcceptTimeout.java:
16 )
setSoTimeout 方法可以在ServerSocket 对象绑定端口之前调用,也以在绑定端口之后调用。如下面的代码也是正确的:
ServerSocket serverSocket  =   new  ServerSocket();
serverSocket.setSoTimeout(
3000 );
serverSocket.bind(
new  InetSocketAddress( 1234 ));
二、 SO_REUSEADDR 选项
SO_REUSEADDR 选项决定了一个端口是否可以被绑定多次。可以通过SeverSocket 类的两个方法(setReuseAddres getReuseAddress) 来设置和获得SO_TIMEOUT 选项的值,这两个方法的定义如下:
public   void  setReuseAddress( boolean  on)  throws  SocketException 
public   boolean  getReuseAddress()  throws  SocketException 
在大多数操作系统中都不允许一个端口被多次绑定。如果一个ServerSocket 对象绑定了已经被占用的端口,那么ServerSocket 的构造方法或bind 方法就会抛出一个BindException 异常。
Java 提供这个选项的主要目的是为了防止由于频繁绑定释放一个固定端口而使系统无法正常工作。当ServerSocket 对象关闭后,如果ServerSocket 对象中仍然有未处理的数据,那么它所绑定的端口可能在一段时间内不会被释放。这就会造成其他的ServerSocket 对象无法绑定这个端口。在设置这个选项时,如果某个端口是第一次被绑定,无需调用setReuseAddress 方法,而再次绑定这个端口时,必须使用setReuseAddress 方法将这个选项设为true 。而且这个方法必须在调用bind 方法之前调用。下面的代码 演示了如何设置和获得这个选项的值:
package  server;

import  java.net. * ;

public   class  TestReuseAddr1
{
    
public   static   void  main(String[] args)  throws  Exception
    {
        ServerSocket serverSocket1 
=   new  ServerSocket( 1234 );
        System.out.println(serverSocket1.getReuseAddress());
        
        ServerSocket serverSocket2 
=   new  ServerSocket();
        serverSocket2.setReuseAddress(
true );
        serverSocket2.bind(
new  InetSocketAddress( 1234 ));
        
        ServerSocket serverSocket3 
=   new  ServerSocket();
        serverSocket3.setReuseAddress(
true );
        serverSocket3.bind(
new  InetSocketAddress( 1234 ));
    }
}
运行结果:false
在上面代码中 第一次绑定端口1234 ,因此,serverSocket1 对象无需设置SO_REUSEADDR 选项(这个选项在大多数操作系统上的默认值是false )。而serverSocket2 serverSocket3 并不是第一次绑定端口1234 ,因此,必须设置这两个对象的SO_REUSEADDR 值为true 。在设置SO_REUSEADDR 选项时要注意,必须在ServerSocket 对象绑定端口之前设置这个选项。
    也许有的读者可能有这样的疑问。如果多个ServerSocket 对象同时绑定到一个端口上,那么当客户端向这个端口发出请求时,该由哪个ServerSocket 对象来接收客户端请求呢?在给出答案之前,让我们先看看下面的代码 的输出结果是什么。 
package  server;

import  java.net. * ;

public   class  TestReuseAddr2  extends  Thread
{
    String s;
    
public   void  run()
    {
        
try
        {
            ServerSocket serverSocket 
=   new  ServerSocket();
            serverSocket.setReuseAddress(
true );
            serverSocket.bind(
new  InetSocketAddress( 1234 ));
            Socket socket 
=  serverSocket.accept();
            System.out.println(s 
+   " "   +  socket);
            socket.close();
            serverSocket.close();
        }
        
catch  (Exception e)
        {
        }
    }
    
public  TestReuseAddr2(String s)
    {
        
this .s  =  s;
    }
    
public   static   void  main(String[] args)
    {
        
for  ( int  i  =   1 ; i  <=   5 ; i ++ )
            
new  TestReuseAddr2( " ServerSocket "   +  i).start();
    }
}
执行下面的命令:
java server.TestReuseAddr2

    连续执行5次下面的命令:
telnet localhost  1234
执行结果:
ServerSocket1:Socket[addr =/ 127.0 . 0.1 ,port = 11724 ,localport = 1234 ]
ServerSocket3:Socket[addr
=/ 127.0 . 0.1 ,port = 11725 ,localport = 1234 ]
ServerSocket5:Socket[addr
=/ 127.0 . 0.1 ,port = 11726 ,localport = 1234 ]
ServerSocket2:Socket[addr
=/ 127.0 . 0.1 ,port = 11727 ,localport = 1234 ]
ServerSocket4:Socket[addr
=/ 127.0 . 0.1 ,port = 11728 ,localport = 1234 ]
    上面的运行结果只是一种可能,如果多次按着上面的步骤操作,可能得到不同的运行结果。由此可以断定,当多个ServerSocket 对象同时绑定一个端口时,系统会随机选择一个ServerSocket 对象来接收客户端请求。但要注意,这个接收客户端请求的ServerSocket 对象必须关闭(如019 行如示),才能轮到其他的ServerSocket 对象接收客户端请求。如果不关闭这个ServerSocket 对象,那么其他的ServerSocket 对象将永远无法接收客户端请求。读者可以将  serverSocket.close() 去掉,再执行上面操作步骤,看看会有什么结果。

三、 SO_RCVBUF 选项
可以通过SeverSocket 类的两个方法(setReceiveBufferSize getReceiveBufferSize) 来设置和获得SO_RCVBUF 选项的值,这两个方法的定义如下:
public   synchronized   void  setReceiveBufferSize ( int  size)  throws  SocketException
public   synchronized   int  getReceiveBufferSize()  throws  SocketException
     其中 size 参数表示接收缓冲区的大小,单位是字节。设置了 ServerSocket 类的 SO_RCVBUF 选项,就相当于设置了 Socket 对象的接收缓冲区大小。这个 Socket 对象是由 accept 返回的。下面积代码 演示了如何使用这两个方法来设置和获得接收缓冲区的大小:

package  server;

import  java.net. * ;

public   class  TestReceiveBufferSize
{
    
public   static   void  main(String[] args)  throws  Exception
    {
        ServerSocket serverSocket 
=   new  ServerSocket( 1234 );
        serverSocket.setReceiveBufferSize(
2048 );  //  将接收缓冲区设为2K
         while  ( true )
        {
            Socket socket 
=  serverSocket.accept();
            
//  如果客户端请求使用的是本地IP地址,重新将Socket对象的接
            
//  收缓冲区设为1K            
             if  (socket.getInetAddress().isLoopbackAddress())
                socket.setReceiveBufferSize(
1024 );
            System.out.println(
" serverSocket: "
                            
+  serverSocket.getReceiveBufferSize());
            System.out.println(
" socket: "   +  socket.getReceiveBufferSize());
            socket.close();
        }
    }
}

执行如下命令:
java server.TestReceiveBufferSize
执行如下三个命令 (192.168.18.100为本机IP地址):
telnet  192.168 . 18.100   1234
telnet localhost 
1234
telnet 
192.168 . 18.100   1234
运行结果:
serverSocket: 2048
socket:
2048
serverSocket:
2048
socket:
1024
serverSocket:
2048
socket:
2048
从上面的运行结果可以看出,在执行telnet localhost 1234 命令后,由于localhost 是本地地址,因此程序通过 Socket 对象的接收缓冲区设为1024 ,而在执行其他两条命令后,由于192.168.18.100 不是本机地址,所以Socket 对象的接收缓冲区仍然保留着serverSocket 的值:2048 。因此,我们可以得出一个结论,设置ServerSocket 对象的接收缓冲区就相当于设置了所有从accept 返回的Socket 对象的接收缓冲区,只要不单独对某个Socket 对象重新设置,这些Socket 对象的接收缓冲区就会都保留这个值。
无论在ServerSocket 对象绑定到端口之前还是之后设置SO_RCVBUF 选项都有效,但如果要设置大于64K 的接收缓冲区时,就必须在ServerSocket 对象绑定端口之前设置SO_RCVBUF 选项。如下面的代码将接收缓冲区的大小设为100K
ServerSocket serverSocket  =   new  ServereSocket();
serverSocket. setReceiveBufferSize(
100   *   1024 );   //  将接收缓冲区的大小设为100K。
serverSocket.bind( new  InetSocketAddress( 1234 ));
    一般情况下,并不需要设置这个选项,它的默认值(一般为8K )足可以满足大多数情况。但有时为了适应特殊的需要,必须更改接收缓冲区的值。如在一些网络游戏中,需要实时地向服务器传送各种动作、指令信息。这就需要将接收缓冲区设小一点。这样可以在一定程度上增加游戏客户端的灵敏度。如果需要传送大量的数据,如HTTP FTP 等协议。这就需要较大的接收缓冲区。

四、设置 ServerSocket 的性能偏好  
Java SE5.0及以上版本 中为ServerSocket 类增加了一个setPerformancePreferences 方法。这个和方法和Socket 类中的setPerformancePreferences 的作用一样,用来设置连接时间、延迟和带宽的相对重要性。setPerformancePerferences 方法的定义如下:
public   void  setPerformancePreferences( int  connectionTime,  int  latency,  int  bandwidth)







 本文转自 androidguy 51CTO博客,原文链接: http://blog.51cto.com/androidguy/214346 ,如需转载请自行联系原作者
相关文章
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
662 11
|
12月前
|
JSON 移动开发 网络协议
Java网络编程:Socket通信与HTTP客户端
本文全面讲解Java网络编程,涵盖TCP与UDP协议区别、Socket编程、HTTP客户端开发及实战案例,助你掌握实时通信、文件传输、聊天应用等场景,附性能优化与面试高频问题解析。
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
439 1
|
10月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
407 1
|
10月前
|
机器学习/深度学习 分布式计算 Java
Java与图神经网络:构建企业级知识图谱与智能推理系统
图神经网络(GNN)作为处理非欧几里得数据的前沿技术,正成为企业知识管理和智能推理的核心引擎。本文深入探讨如何在Java生态中构建基于GNN的知识图谱系统,涵盖从图数据建模、GNN模型集成、分布式图计算到实时推理的全流程。通过具体的代码实现和架构设计,展示如何将先进的图神经网络技术融入传统Java企业应用,为构建下一代智能决策系统提供完整解决方案。
766 0
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
707 23
|
人工智能 运维 API
云栖大会 | Terraform从入门到实践:快速构建你的第一张业务网络
云栖大会 | Terraform从入门到实践:快速构建你的第一张业务网络
650 1
|
安全 网络协议 Java
Java网络编程封装
Java网络编程封装原理旨在隐藏底层通信细节,提供简洁、安全的高层接口。通过简化开发、提高安全性和增强可维护性,封装使开发者能更高效地进行网络应用开发。常见的封装层次包括套接字层(如Socket和ServerSocket类),以及更高层次的HTTP请求封装(如RestTemplate)。示例代码展示了如何使用RestTemplate简化HTTP请求的发送与处理,确保代码清晰易维护。
|
机器学习/深度学习 资源调度 算法
图卷积网络入门:数学基础与架构设计
本文系统地阐述了图卷积网络的架构原理。通过简化数学表述并聚焦于矩阵运算的核心概念,详细解析了GCN的工作机制。
1299 3
图卷积网络入门:数学基础与架构设计
|
JSON Dart 前端开发
鸿蒙应用开发从入门到入行 - 篇7:http网络请求
在本篇文章里,您将掌握鸿蒙开发工具DevEco的基本使用、ArkUI里的基础组件,并通过制作一个简单界面掌握使用
1138 8