`
seara
  • 浏览: 648922 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

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

阅读更多
本文为原创,如需转载,请注明作者和出处,谢谢!


上一篇:Java网络编程从入门到精通(17):Socket类的getter和setter方法(1)

二、
用于获得和设置Socket选项的gettersetter方法

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


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicfinalstaticintTCP_NODELAY=0x0001;

publicfinalstaticintSO_REUSEADDR=0x04;

publicfinalstaticintSO_LINGER=0x0080;

publicfinalstaticintSO_TIMEOUT=0x1006;

publicfinalstaticintSO_SNDBUF=0x1001;

publicfinalstaticintSO_RCVBUF=0x1002;

publicfinalstaticintSO_KEEPALIVE=0x0008;

publicfinalstaticintSO_OOBINLINE=0x1003;

有趣的是,这8个选项除了第一个没在SO前缀外,其他7个选项都以SO作为前缀。其实这个SO就是Socket Option的缩写;因此,在Java中约定所有以SO为前缀的常量都表示Socket选项;当然,也有例外,如TCP_NODELAY。在Socket类中为每一个选项提供了一对getset方法,分别用来获得和设置这些选项。

1. TCP_NODELAY

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicbooleangetTcpNoDelay()throwsSocketException
publicvoidsetTcpNoDelay(booleanon)throwsSocketException

在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字节(IP+TCP头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法;在默认情况下,Nagle算法是开启的。

这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实现性的要求比较高的情况下(如游戏、Telnet等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle算法时就使用它,不需要时就关闭它。而使用setTcpToDelay正好可以满足这个需求。当使用setTcpNoDelay(true)Nagle算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出去。

2. SO_REUSEADDR

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicbooleangetReuseAddress()throwsSocketException
publicvoidsetReuseAddress(booleanon)throwsSocketException

通过这个选项,可以使多个Socket对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close方法关闭Socket连接后,Socket对象所绑定的端口并不一定马上释放;系统有时在Socket连接关闭才会再确认一下是否有因为延迟面未到达的数据包,这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket类时完全不会感觉到。

这种处理机制对于随机绑定端口的Socket对象没有什么影响,但对于绑定在固定端口的Socket对象就可能会抛出“Address already in use: JVM_Bind”例外。因此,使用这个选项可以避免个例外的发生。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->package mynet;

importjava.net.*;
importjava.io.*;

publicclass Test
{
publicstaticvoidmain(String[]args)
{
Socketsocket1
=newSocket();
Socketsocket2
=newSocket();
try
{
socket1.setReuseAddress(
true);
socket1.bind(
newInetSocketAddress("127.0.0.1",88));
System.out.println(
"socket1.getReuseAddress():"
+socket1.getReuseAddress());
socket2.bind(
newInetSocketAddress("127.0.0.1",88));
}
catch(Exceptione)
{
System.out.println(
"error:"+e.getMessage());
try
{
socket2.setReuseAddress(
true);
socket2.bind(
newInetSocketAddress("127.0.0.1",88));
System.out.println(
"socket2.getReuseAddress():"
+socket2.getReuseAddress());
System.out.println(
"端口88第二次绑定成功!");
}
catch(Exceptione1)
{
System.out.println(e.getMessage());
}
}
}
}

上面的代码的运行结果如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->socket1.getReuseAddress():true
error:Addressalreadyinuse: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

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicintgetSoLinger()throwsSocketException
publicvoidsetSoLinger(booleanon,intlinger)throwsSocketException

这个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分钟:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->if(socket.getSoLinger()==-1)socket.setSoLinger(true,60);

4. SO_TIMEOUT

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicintgetSoTimeout()throwsSocketException
publicvoidsetSoTimeout(inttimeout)throwsSocketException

这个Socket选项在前面已经讨论过。可以通过这个选项来设置读取数据超时。当输入流的read方法被阻塞时,如果设置timeouttimeout的单位是毫秒),那么系统在等待了timeout毫秒后会抛出一个InterruptedIOException例外。在抛出例外后,输入流并未关闭,你可以继续通过read方法读取数据。

如果将timeout设为0,就意味着read将会无限等待下去,直到服务端程序关闭这个Socket。这也是timeout的默认值。如下面的语句将读取数据超时设为30秒:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->socket1.setSoTimeout(30*1000);

当底层的Socket实现不支持SO_TIMEOUT选项时,这两个方法将抛出SocketException例外。不能将timeout设为负数,否则setSoTimeout方法将抛出IllegalArgumentException例外。

5. SO_SNDBUF

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicintgetSendBufferSize()throwsSocketException
publicvoidsetSendBufferSize(intsize)throwsSocketException

在默认情况下,输出流的发送缓冲区是8096个字节(8K)。这个值是Java所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用setSendBufferSize方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。

如果底层的Socket实现不支持SO_SENDBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setSendBufferedSize方法将抛出IllegalArgumentException例外。

6. SO_RCVBUF


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicintgetReceiveBufferSize()throwsSocketException
publicvoidsetReceiveBufferSize(intsize)throwsSocketException

在默认情况下,输入流的接收缓冲区是8096个字节(8K)。这个值是Java所建议的输入缓冲区的大小。如果这个默认值不能满足要求,可以用setReceiveBufferSize方法来重新设置缓冲区的大小。但最好不要将输入缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。

如果底层的Socket实现不支持SO_RCVBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setReceiveBufferSize方法将抛出IllegalArgumentException例外。

7. SO_KEEPALIVE

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicbooleangetKeepAlive()throwsSocketException
publicvoidsetKeepAlive(booleanon)throwsSocketException

如果将这个Socket选项打开,客户端Socket每隔段的时间(大约两个小时)就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,在大约11分钟后,客户端Socket再发送一个数据包,如果在12分钟内,服务器还没响应,那么客户端Socket将关闭。如果将Socket选项关闭,客户端Socket在服务器无效的情况下可能会长时间不会关闭。SO_KEEPALIVE选项在默认情况下是关闭的,可以使用如下的语句将这个SO_KEEPALIVE选项打开:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->socket1.setKeepAlive(true);

8. SO_OOBINLINE

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicbooleangetOOBInline()throwsSocketException
publicvoidsetOOBInline(booleanon)throwsSocketException

如果这个Socket选项打开,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,而是立即发出。虽然在客户端并不是使用OutputStream向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服务端程序中并不知道由客户端发过来的数据是由OutputStream还是由sendUrgentData发过来的。下面是sendUrgentData方法的声明:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->publicvoidsendUrgentData(intdata)throwsIOException

虽然sendUrgentData的参数dataint类型,但只有这个int类型的低字节被发送,其它的三个字节被忽略。下面的代码演示了如何使用SO_OOBINLINE选项来发送单字节数据。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->package mynet;

importjava.net.*;
importjava.io.*;

class Server
{
publicstaticvoidmain(String[]args)throwsException
{
ServerSocketserverSocket
=newServerSocket(1234);
System.out.println(
"服务器已经启动,端口号:1234");
while(true)
{
Socketsocket
=serverSocket.accept();
socket.setOOBInline(
true);
InputStreamin
=socket.getInputStream();
InputStreamReaderinReader
=newInputStreamReader(in);
BufferedReaderbReader
=newBufferedReader(inReader);
System.out.println(bReader.readLine());
System.out.println(bReader.readLine());
socket.close();
}
}
}
publicclass Client
{
publicstaticvoidmain(String[]args)throwsException
{
Socketsocket
=newSocket("127.0.0.1",1234);
socket.setOOBInline(
true);
OutputStreamout
=socket.getOutputStream();
OutputStreamWriteroutWriter
=newOutputStreamWriter(out);
outWriter.write(
67);//向服务器发送字符"C"
outWriter.write("helloworld\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,因ServerClient类必须在同一台机器上运行。

运行Server

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->javamynet.Server

运行Client

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->javamynet.Client

在服务端控制台的输出结果

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->服务器已经启动,端口号:1234
ABChelloworld
中国

在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类中向服务器发送"中国"字符串。由于""是由214208两个字节组成的;而""是由185250两个字节组成的;因此,可分别发送这四个字节来传送"中国"字符串。

注意:在使用setOOBInline方法打开SO_OOBINLINE选项时要注意是必须在客户端和服务端程序同时使用setOOBInline方法打开这个选项,否则无法命名用sendUrgentData来发送数据。

下一篇:Java网络编程从入门到精通(19):套接字(Socket)的异常



国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

分享到:
评论

相关推荐

    JAVA网络编程从入门到精通

    ### JAVA网络编程从入门到精通知识点详解 #### 一、Internet地址概述 互联网中的每一台设备都需要有一个唯一的标识符——IP地址。当前广泛使用的IPv4地址由四个字节组成,而未来的趋势是采用16个字节的IPv6地址。 ...

    java普通类编译成json但只是当前类的有getter、setter方法的版本

    通过以上步骤,我们实现了Java类到JSON的转换,仅保留了具有getter和setter方法的属性。这样可以确保JSON数据的精简,同时符合业务需求。在实际开发中,还可以根据需要进一步配置Jackson,比如忽略特定字段、自定义...

    java * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class

    提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,java * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,java * 反射工具类. 提供调用getter/...

    eclipse 自动为getter和setter 添加中文注释

    在Java编程中,getter和setter方法是用于封装对象属性的重要工具。Eclipse作为一个强大的集成开发环境(IDE),提供了丰富的代码生成功能,包括自动为getter和setter添加注释。本篇文章将详细探讨如何在Eclipse中...

    iOS getter setter方法

    在iOS开发中,getter和setter方法是Objective-C和Swift中对象属性访问的重要组成部分。它们用于获取(get)和设置(set)对象的属性值。本文将深入探讨getter和setter的概念、作用以及如何在代码中使用它们。 首先...

    java 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class

    提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实...

    自动生成带注释的getter和setter方法(Intellij、AndroidStudio插件).zip

    标题中的“自动生成带注释的getter和setter方法(Intellij、AndroidStudio插件)”指的是在编程过程中,开发者可以利用特定的插件自动化生成Java Bean模式中的getter和setter方法,并且这些方法会带有注释。...

    自动生成带注释的getter和setter方法(注释)插件

    在编程领域,尤其是在Java开发中,getter和setter方法是面向对象设计的重要组成部分,它们用于封装对象的属性,确保数据的安全性。然而,手动编写这些方法可能会耗费大量时间。为了解决这个问题,开发者们通常会利用...

    eclipse 自动 getter setter 注释

    在Java编程中,getter和setter方法是面向对象设计原则中的封装特性的重要体现。它们用于访问和修改类的私有成员变量,确保数据的安全性。Eclipse是一款广泛使用的集成开发环境(IDE),它提供了丰富的代码自动补全和...

    Vue 理解之白话 getter/setter详解

    在深入学习Vue框架的过程中,理解和掌握getter和setter机制是非常重要的。Vue通过响应式原理,将普通的JavaScript对象属性转换成getter和setter,以此来实现数据的双向绑定和状态管理。下面我们将详细地探讨Vue中的...

    Eclipse setter/getter 注释成字段的注释

    在Java编程中,Eclipse是一款广泛使用的集成开发环境(IDE),它提供了许多便捷的功能,包括自动生成getter和setter方法。这些方法通常用于封装类的属性,以保护数据并实现对象的访问控制。当我们为类的每个字段添加...

    解决IDEA生成getter,setter方法不带注释问题,非常实用,带截图操作文档包会,为广大程序员节省时间成本,我也花了很多时间

    然而,在实际开发过程中,有时我们需要自动生成带有注释的getter和setter方法,以提高代码的可读性和规范性。IDEA默认生成的getter和setter方法可能不包含注释,这可能给团队协作带来不便。本文将详细解释如何配置...

    IntelliJ IDEA快速创建getter和setter方法

    在面向对象编程中,getter 和 setter 方法是用于访问和修改对象的属性的重要方法。getter 方法用于获取对象的属性值,而 setter 方法用于设置对象的属性值。通常情况下,开发者需要手动编写 getter 和 setter 方法,...

    idea getter/setter插件

    在Java编程中,getter和setter方法是对象属性访问的重要组成部分,它们提供了封装和数据访问控制。为了提高开发效率,IntelliJ IDEA,一款广受欢迎的Java集成开发环境(IDE),提供了一系列的插件来简化getter和...

    java web入门到精通的光盘源码

    "Java Web入门到精通的光盘源码"是一份宝贵的资源,它包含了从初学者到高级开发者在学习过程中可能会遇到的各种示例代码,旨在帮助读者快速理解和掌握Java Web开发。 1. **Servlet**:Servlet是Java Web的核心组件...

    Intellij IDEA 插件,可以将实例变量上的JavaDoc暴露到Getter和Setter的定制模版中

    本篇将详细讲解一个名为"Intellij Plugin Expose Javadoc"的插件,它能够帮助开发者将实例变量上的JavaDoc注释自动应用到对应的Getter和Setter方法中,从而提高代码的可读性和维护性。 JavaDoc是一种在Java源代码中...

    eclipse中setter、getter注释

    在Java编程语言中,getter和setter方法是面向对象设计的一部分,它们主要用于封装对象的属性,以保护数据并提供访问控制。Eclipse是一款流行的集成开发环境(IDE),它提供了丰富的自动代码生成功能,包括生成getter...

    [从初学到精通系列:Java从初学到精通].辛立伟等.扫描版

    根据提供的文件信息,我们可以推断出这是一本关于Java编程语言的学习资料——《从初学到精通系列:Java从初学到精通》,作者为辛立伟等人。本书旨在帮助读者从零基础开始逐步掌握Java编程技术,并最终达到精通的程度...

    vim-java-get-set:为 Java 类生成 getter 和 setter 的插件

    `vim-java-get-set` 是一个 Vim 编辑器的插件,专为 Java 开发者设计,能够自动化生成 Java 类的 getter 和 setter 方法。在软件开发中,getter 和 setter 方法是面向对象编程中的常见实践,用于封装类的属性,提供...

    java简便方法引入getter/setter/tostring等方法

    java 的eclipse或idea等 定义变量时 直接引入lombok.jar包 在类外部 引入@AllArgsConstructor @NoArgsConstructor @Data 简便方法引入getter/setter/tostring等方法

Global site tag (gtag.js) - Google Analytics