Java网络编程从入门到精通(18):Socket类的getter和setter方法(2)

简介:
二、用于获得和设置Socket选项的gettersetter方法
Socket 选择可以指定Socket 类发送和接受数据的方式。在JDK1.4 中共有8 Socket 选择可以设置。这8 个选项都定义在java.net.SocketOptions 接口中。定义如下:

     public   final   static   int  TCP_NODELAY  =   0x0001 ;

    public   final   static   int  SO_REUSEADDR  =   0x04 ;

    public   final   static   int  SO_LINGER  =   0x0080 ;

    
public   final   static   int  SO_TIMEOUT  =   0x1006 ;

    
public   final   static   int  SO_SNDBUF  =   0x1001 ;

    
public   final   static   int  SO_RCVBUF  =   0x1002 ;

    
public   final   static   int  SO_KEEPALIVE  =   0x0008 ;

    
public   final   static   int  SO_OOBINLINE  =   0x1003 ;
     有趣的是,这8 个选项除了第一个没在SO 前缀外,其他7 个选项都以SO 作为前缀。其实这个SO 就是 Socket Option 的缩写;因此,在Java 中约定所有以SO 为前缀的常量都表示Socket 选项;当然,也有例外,如TCP_NODELAY 。在Socket 类中为每一个选项提供了一对get set 方法,分别用来获得和设置这些选项。
1. TCP_NODELAY
public   boolean  getTcpNoDelay()  throws  SocketException
public   void  setTcpNoDelay( boolean  on)  throws  SocketException
在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1 个字节,而数据包的头却有几十个字节(IP +TCP 头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle 算法;在默认情况下,Nagle 算法是开启的。
这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实现性的要求比较高的情况下(如游戏、Telnet 等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle 算法时就使用它,不需要时就关闭它。而使用setTcpToDelay 正好可以满足这个需求。当使用setTcpNoDelay(true) Nagle 算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出去。
2.  SO_REUSEADDR
public   boolean  getReuseAddress()  throws  SocketException           
public   void  setReuseAddress( boolean  on)  throws  SocketException
通过这个选项,可以使多个Socket 对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close 方法关闭Socket 连接后,Socket 对象所绑定的端口并不一定马上释放;系统有时在Socket 连接关闭才会再确认一下是否有因为延迟面未到达的数据包,这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket 类时完全不会感觉到。
这种处理机制对于随机绑定端口的Socket 对象没有什么影响,但对于绑定在固定端口的Socket 对象就可能会抛出“Address already in use: JVM_Bind” 例外。因此,使用这个选项可以避免个例外的发生。
package  mynet;

import  java.net. * ;
import  java.io. * ;

public   class  Test
{
    
public   static   void  main(String[] args)
    {
        Socket socket1 
=   new  Socket();
        Socket socket2 
=   new  Socket();
        
try
        {
            socket1.setReuseAddress(
true );
            socket1.bind(
new  InetSocketAddress( " 127.0.0.1 " 88 ));
            System.out.println(
" socket1.getReuseAddress(): "
                    
+  socket1.getReuseAddress());
            socket2.bind(
new  InetSocketAddress( " 127.0.0.1 " 88 ));
        }
        
catch  (Exception e)
        {
            System.out.println(
" error: "   +  e.getMessage());
            
try
            {
                socket2.setReuseAddress(
true );
                socket2.bind(
new  InetSocketAddress( " 127.0.0.1 " 88 ));
                System.out.println(
" socket2.getReuseAddress(): "
                        
+  socket2.getReuseAddress());
                System.out.println(
" 端口88第二次绑定成功! " );
            }
            
catch  (Exception e1)
            {
                System.out.println(e.getMessage());
            }
        }
    }
}
上面的代码的运行结果如下:
socket1.getReuseAddress():true
error:Address already in use: JVM_Bind
socket2.getReuseAddress():true
端口88第二次绑定成功!
使用SO_REUSEADDR选项时有两点需要注意:
1.  必须在调用bind方法之前使用setReuseAddress方法来打开SO_REUSEADDR选项。因此,要想使用SO_REUSEADDR选项,就不能通过Socket类的构造方法来绑定端口。
2.  必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR选项都打开才能起作用。如在例程4-12中,socket1和socket2都使用了setReuseAddress方法打开了各自的SO_REUSEADDR选项。
3.  SO_LINGER
public   int  getSoLinger()  throws  SocketException
public   void  setSoLinger( boolean  on,  int  linger)  throws  SocketException
这个Socket 选项可以影响close 方法的行为。在默认情况下,当调用close 方法后,将立即返回;如果这时仍然有未被送出的数据包,那么这些数据包将被丢弃。如果将linger 参数设为一个正整数n (n 的值最大是65,535) ,在调用close 方法后,将最多被阻塞n 秒。在这n 秒内,系统将尽量将未送出的数据包发送出去;如果超过了n 秒,如果还有未发送的数据包,这些数据包将全部被丢弃;而close 方法会立即返回。如果将linger 设为0 ,和关闭SO_LINGER 选项的作用是一样的。
如果底层的Socket 实现不支持SO_LINGER 都会抛出SocketException 例外。当给linger 参数传递负数值时,setSoLinger 还会抛出一个IllegalArgumentException 例外。可以通过getSoLinger 方法得到延迟关闭的时间,如果返回-1 ,则表明SO_LINGER 是关闭的。例如,下面的代码将延迟关闭的时间设为1 分钟:
if (socket.getSoLinger()  ==   - 1 ) socket.setSoLinger( true 60 );
4.  SO_TIMEOUT
public   int  getSoTimeout()  throws  SocketException
public   void  setSoTimeout( int  timeout)  throws  SocketException
这个Socket 选项在前面已经讨论过。可以通过这个选项来设置读取数据超时。当输入流的read 方法被阻塞时,如果设置timeout timeout 的单位是毫秒),那么系统在等待了timeout 毫秒后会抛出一个InterruptedIOException 例外。在抛出例外后,输入流并未关闭,你可以继续通过read 方法读取数据。
如果将timeout 设为0 ,就意味着read 将会无限等待下去,直到服务端程序关闭这个Socket 。这也是timeout 的默认值。如下面的语句将读取数据超时设为30 秒:
socket1.setSoTimeout( 30   *   1000 );
当底层的Socket 实现不支持SO_TIMEOUT 选项时,这两个方法将抛出SocketException 例外。不能将timeout 设为负数,否则setSoTimeout 方法将抛出IllegalArgumentException 例外。
5.  SO_SNDBUF
public   int  getSendBufferSize()  throws  SocketException
public   void  setSendBufferSize( int  size)  throws  SocketException
在默认情况下,输出流的发送缓冲区是8096 个字节(8K )。这个值是Java 所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用setSendBufferSize 方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。
如果底层的Socket 实现不支持SO_SENDBUF 选项,这两个方法将会抛出SocketException 例外。必须将size 设为正整数,否则setSendBufferedSize 方法将抛出IllegalArgumentException 例外。
6.  SO_RCVBUF

public   int  getReceiveBufferSize()  throws  SocketException
public   void  setReceiveBufferSize( int  size)  throws  SocketException
在默认情况下,输入流的接收缓冲区是8096 个字节(8K )。这个值是Java 所建议的输入缓冲区的大小。如果这个默认值不能满足要求,可以用setReceiveBufferSize 方法来重新设置缓冲区的大小。但最好不要将输入缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。
如果底层的Socket 实现不支持SO_RCVBUF 选项,这两个方法将会抛出SocketException 例外。必须将size 设为正整数,否则setReceiveBufferSize 方法将抛出IllegalArgumentException 例外。
7.  SO_KEEPALIVE
public   boolean  getKeepAlive()  throws  SocketException
public   void  setKeepAlive( boolean  on)  throws  SocketException
如果将这个Socket 选项打开,客户端Socket 每隔段的时间(大约两个小时)就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,在大约11 分钟后,客户端Socket 再发送一个数据包,如果在12 分钟内,服务器还没响应,那么客户端Socket 将关闭。如果将Socket 选项关闭,客户端Socket 在服务器无效的情况下可能会长时间不会关闭。SO_KEEPALIVE 选项在默认情况下是关闭的,可以使用如下的语句将这个SO_KEEPALIVE 选项打开:
socket1.setKeepAlive( true );
8.  SO_OOBINLINE
  public   boolean  getOOBInline()  throws  SocketException
 
public   void  setOOBInline( boolean  on)  throws  SocketException
如果这个Socket 选项打开,可以通过Socket 类的sendUrgentData 方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,而是立即发出。虽然在客户端并不是使用OutputStream 向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服务端程序中并不知道由客户端发过来的数据是由OutputStream 还是由sendUrgentData 发过来的。下面是sendUrgentData 方法的声明:
public   void  sendUrgentData( int  data)  throws  IOException
虽然sendUrgentData 的参数data int 类型,但只有这个int 类型的低字节被发送,其它的三个字节被忽略。下面的代码 演示了如何使用SO_OOBINLINE 选项来发送单字节数据。
package  mynet;

import  java.net. * ;
import  java.io. * ;

class  Server
{
    
public   static   void  main(String[] args)  throws  Exception
    {
        ServerSocket serverSocket 
=   new  ServerSocket( 1234 );
        System.out.println(
" 服务器已经启动,端口号:1234 " );
        
while  ( true )
        {
            Socket socket 
=  serverSocket.accept();
            socket.setOOBInline(
true );
            InputStream in 
=  socket.getInputStream();
            InputStreamReader inReader 
=   new  InputStreamReader(in);
            BufferedReader bReader 
=   new  BufferedReader(inReader);
            System.out.println(bReader.readLine());
            System.out.println(bReader.readLine());
            socket.close();
        }
    }
}
public   class  Client
{
    
public   static   void  main(String[] args)  throws  Exception
    {
        Socket socket 
=   new  Socket( " 127.0.0.1 " 1234 );
        socket.setOOBInline(
true );
        OutputStream out 
=  socket.getOutputStream();
        OutputStreamWriter outWriter 
=   new  OutputStreamWriter(out);
        outWriter.write(
67 );               //  向服务器发送字符"C"
        outWriter.write( " hello world\r\n " );
        socket.sendUrgentData(
65 );         //  向服务器发送字符"A"
        socket.sendUrgentData( 322 );         //  向服务器发送字符"B"
        outWriter.flush();
        socket.sendUrgentData(
214 );        //  向服务器发送汉字”中”
        socket.sendUrgentData( 208 );
        socket.sendUrgentData(
185 );        //  向服务器发送汉字”国”
        socket.sendUrgentData( 250 );
        socket.close();
    }
}
由于运行上面的代码 需要一个服务器类,因此,在 加了一个类名为Server 的服务器类,关于服务端套接字的使用方法将会在后面的文章中详细讨论。在类Server类 中只使用了ServerSocket 类的accept 方法接收客户端的请求。并 从客户端传来的数据中读取两行字符串,并显示在控制台上。
测试
由于本例使用了127.0.0.1 ,因Server Client类 必须在同一台机器上运行。
运行Server
java mynet.Server
运行Client
java mynet.Client
在服务端控制台的输出结果
服务器已经启动,端口号: 1234
ABChello world
中国
在ClienT类中使用了sendUrgentData 方法向服务器发送了字符'A'(65) 'B'(66) 。但发送'B'时 实际发送的是322 ,由于sendUrgentData 只发送整型数的低字节。因此,实际发送的是66 。十进制整型322 的二进制形式如图1 所示。
 
图1  十进制整型322的二进制形式
      从图1可以看出,虽然322分布在了两个字节上,但它的低字节仍然是66。
在Client类中使用flush 将缓冲区中的数据发送到服务器。我们可以从输出结果发现一个问题,在Client类中 先后向服务器发送了'C' "hello world"r"n" 'A' 'B' 。而在服务端程序的控制台上显示的却是ABChello world 。这种现象说明使用sendUrgentData 方法发送数据后,系统会立即将这些数据发送出去;而使用write 发送数据,必须要使用flush 方法才会真正发送数据。
在Client类中 向服务器发送" 中国" 字符串。由于" " 是由214 208 两个字节组成的;而" " 是由185 250 两个字节组成的;因此,可分别发送这四个字节来传送" 中国" 字符串。
注意:在使用setOOBInline方法打开SO_OOBINLINE选项时要注意是必须在客户端和服务端程序同时使用setOOBInline方法打开这个选项,否则无法命名用sendUrgentData来发送数据。




 本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/214436 ,如需转载请自行联系原作者
相关文章
|
15天前
|
Java
Java——方法的引用
方法引用允许将已有方法作为函数式接口的实现。使用“::”符号,需具备函数式接口,被引用的方法须存在且参数和返回值需与抽象方法一致。其分类包括:静态方法引用(类::方法名)、成员方法引用(对象::方法名、this::方法名、super::方法名)和构造方法引用(类名::new)。方法引用提高了代码的简洁性和可读性,减少了样板代码。
29 13
Java——方法的引用
|
11天前
|
Java
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
本文介绍了Java中抽象类和抽象方法的使用,以及ArrayList的基本操作,包括添加、获取、删除元素和判断列表是否为空。
13 2
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
|
7天前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
18 4
|
9天前
|
安全 Java API
Java根据URL获取文件内容的实现方法
此示例展示了如何安全、有效地根据URL获取文件内容。它不仅展现了处理网络资源的基本技巧,还体现了良好的异常处理实践。在实际开发中,根据项目需求,你可能还需要添加额外的功能,如设置连接超时、处理HTTP响应码等。
44 4
|
15天前
|
Java API
Java方法的优缺点
Java 方法是编程的基本构建块,具有代码重用性、模块化、易于调试、增强可读性、支持重载和可变参数、封装性及静态与实例方法的灵活性等优点,但也存在性能开销、过度抽象、限制使用环境、参数传递开销、命名冲突和堆栈溢出等缺点。合理设计方法可确保代码高效且易维护。
|
11天前
|
安全 Java
java调用方法
java调用方法
18 4
|
11天前
|
Java 索引
java基础扫盲-String类常用的方法
java基础扫盲-String类常用的方法
|
3天前
|
安全 网络协议 网络安全
网络安全与信息安全:漏洞、加密与意识的三重奏
【9月更文挑战第32天】在数字世界的交响乐中,网络安全是那不可或缺的乐章。本文将带您深入探索网络安全的三大主题:网络漏洞的识别与防范、加密技术的奥秘以及安全意识的重要性。通过深入浅出的方式,我们将一起揭开这些概念的神秘面纱,并学习如何在实际生活中应用它们来保护自己的数字足迹。让我们开始这场既刺激又富有教育意义的旅程,提升个人和组织的网络安全防御能力。
|
1天前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
【9月更文挑战第34天】在数字化时代,网络安全与信息安全的重要性日益凸显。本文将探讨网络安全漏洞、加密技术以及安全意识等关键方面,旨在提升读者对网络安全防护的认识和理解。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调培养良好的安全意识的必要性,本文旨在为读者提供实用的知识和建议,以应对日益复杂的网络威胁。
|
1天前
|
安全 算法 网络安全
网络安全的盾牌:从漏洞到加密,构筑信息安全长城
【9月更文挑战第34天】在数字时代的浪潮中,网络安全成为保护个人和组织数据不受侵犯的关键。本文将深入探讨网络安全中的漏洞发现、利用与防范,介绍加密技术的原理与应用,并强调培养安全意识的重要性。我们将通过实际代码示例,揭示网络攻防的复杂性,并提供实用的防护策略,旨在提升读者对网络安全的认识和应对能力。
28 10
下一篇
无影云桌面