本文为原创,如需转载,请注明作者和出处,谢谢!
上一篇:Java网络编程从入门到精通(17):Socket类的getter和setter方法(1)
二、用于获得和设置Socket选项的getter和setter方法
Socket选择可以指定Socket类发送和接受数据的方式。在JDK1.4中共有8个Socket选择可以设置。这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类中为每一个选项提供了一对get和set方法,分别用来获得和设置这些选项。
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方法被阻塞时,如果设置timeout(timeout的单位是毫秒),那么系统在等待了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的参数data是int类型,但只有这个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,因Server和Client类必须在同一台机器上运行。
运行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类中向服务器发送"中国"字符串。由于"中"是由214和208两个字节组成的;而"国"是由185和250两个字节组成的;因此,可分别发送这四个字节来传送"中国"字符串。
注意:在使用setOOBInline方法打开SO_OOBINLINE选项时要注意是必须在客户端和服务端程序同时使用setOOBInline方法打开这个选项,否则无法命名用sendUrgentData来发送数据。
下一篇:Java网络编程从入门到精通(19):套接字(Socket)的异常
国内最棒的Google Android技术社区(eoeandroid),欢迎访问!《银河系列原创教程》发布《Java Web开发速学宝典》出版,欢迎定购
分享到:
相关推荐
java 的eclipse或idea等 定义变量时 直接引入lombok.jar包 在类外部 引入@AllArgsConstructor @NoArgsConstructor @Data 简便方法引入getter/setter/tostring等方法
java普通类编译成json但只是当前类的有getter、setter方法的版本
eclipse 自动为getter和setter添加中文注释 详细参见 http://blog.csdn.net/kongguoan/article/details/38293137
自动生成带注释的getter和setter方法(注释)插件
主要介绍了Vue getter setter,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
科德尔 具有严格的 getter 和 setter 的 MongoDB 的承诺模型类。 有关使用情况,请检查test文件夹。去做文档添加“类型”检查支持原子更新DynamoDB 适配器执照该程序是免费软件:您可以根据自由软件基金会发布的 GNU...
c++的成员变量的get和set生成器,简单易用。
java代码-【3】定义一个学生类,使用LinkedList对学生类进行管理,执行...(2)方法为:构造方法,getter和setter方法,toString方法 2.定义三个Student类的对象,添加到LinkedList中 3.显示LinkedList中元素的内容
已经拥有一年经验的你真的了解get set方法吗?也许不!以前我以为都有上架项目,这都是太基础的东西,但今天我重新回头看了以前做的笔记,真的是书读百遍,其义自见。
最近因为公司的新项目决定使用Vue.js来做,但在使用的过程中发现了一个有趣的事情,因为发现的这个事情展开了一些对于getter和setter的思考,具体是什么下面通过这篇文章来一起看看吧,有需要的朋友们可以参考学习。
主要介绍了IntelliJ IDEA快速创建getter和setter方法,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
显然这是一个无关IE(高级IE除外)的话题,尽管如此,有兴趣的同学还是一起来认识一下ECMAScript5标准中getter和setter的实现。在一个对象中,操作其中的属性或方法,通常运用最多的就是读(引用)和写了,譬如说o....
gettersAndSetters 使用Getter和Setter的简单方法-Java 这些项目是展示使用getter和setter的简单方法
问题之一是,Java 可能会受到批评,因为它与其他语言不同,它没有真正的 Object 属性,只有诸如方法名称之类的编程习惯用法。 因此,名为 getValue() 的方法可能正在访问属性或调用 Web 服务。 此属性访问实用程序...
FastCoding是一个对Xcode所扩展的插件,主要是根据java的开发工具 eclipse 快速生成属性的getter ,setter 方法有感而发.出于兴趣开发本插件,目前主要功能是根据.h,.m文件声明的属性,自动生成get,set,lazy实例化方法
如何使用它使用 getter/setter 方法创建对象 // Create an object with getter/setter methodvar obj = { name : new getterSetter ( 'John' )} ;// Access the property in read or write modeconsole . log ( obj ...
Intellij 直接将jar包当做插件进行本地安装,重启软件 在实体类中,写好成员的注释,然后Alt+Insert 选择 generateGSDoc即可
通常我们用idea默认生成的getter和setter方法是不带注释的,当然,我们同样可以设置idea像MyEclipse一样生成带有Javadoc的模板,具体设置方法,大家参考下本
PHP类属性的getter和setter的快速生成器。 这基于的扩展名,该扩展名已有3年没有更新了。 我删除了自定义模板功能和所有配置选项。 这是一个简单的扩展,可以完成简单的工作。 我已经更新了扩展以与PHP类型提示...