java-socket-option-解读.docx_第1页
java-socket-option-解读.docx_第2页
java-socket-option-解读.docx_第3页
java-socket-option-解读.docx_第4页
java-socket-option-解读.docx_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

二、用于获得和设置Socket选项的getter和setter方法 Socket选择可以指定Socket类发送和接受数据的方式。在JDK1.4中共有8个Socket选择可以设置。这8个选项都定义在.SocketOptions接口中。定义如下: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_NODELAYpublicbooleangetTcpNoDelay()throwsSocketExceptionpublicvoidsetTcpNoDelay(booleanon)throwsSocketException 在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法;在默认情况下,Nagle算法是开启的。 这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实现性的要求比较高的情况下(如游戏、Telnet等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle算法时就使用它,不需要时就关闭它。而使用setTcpToDelay正好可以满足这个需求。当使用setTcpNoDelay(true)将Nagle算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出去。 2. SO_REUSEADDRpublicbooleangetReuseAddress()throwsSocketExceptionpublicvoidsetReuseAddress(booleanon)throwsSocketException 通过这个选项,可以使多个Socket对象绑定在同一个端口上。其实这样做并没有多大意义,但当使用close方法关闭Socket连接后,Socket对象所绑定的端口并不一定马上释放;系统有时在Socket连接关闭才会再确认一下是否有因为延迟面未到达的数据包,这完全是在底层处理的,也就是说对用户是透明的;因此,在使用Socket类时完全不会感觉到。 这种处理机制对于随机绑定端口的Socket对象没有什么影响,但对于绑定在固定端口的Socket对象就可能会抛出“Address already in use: JVM_Bind”例外。因此,使用这个选项可以避免个例外的发生。package mynet;.*;importjava.io.*;publicclass Testpublicstaticvoidmain(Stringargs)Socketsocket1=newSocket();Socketsocket2=newSocket();trysocket1.setReuseAddress(true);socket1.bind(newInetSocketAddress(,88);System.out.println(socket1.getReuseAddress():+socket1.getReuseAddress();socket2.bind(newInetSocketAddress(,88);catch(Exceptione)System.out.println(error:+e.getMessage();trysocket2.setReuseAddress(true);socket2.bind(newInetSocketAddress(,88);System.out.println(socket2.getReuseAddress():+socket2.getReuseAddress();System.out.println(端口88第二次绑定成功!);catch(Exceptione1)System.out.println(e.getMessage(); 上面的代码的运行结果如下:socket1.getReuseAddress():trueerror:Addressalreadyinuse:JVM_Bindsocket2.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_LINGERpublicintgetSoLinger()throwsSocketExceptionpublicvoidsetSoLinger(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分钟:if(socket.getSoLinger()=-1)socket.setSoLinger(true,60); 4. SO_TIMEOUTpublicintgetSoTimeout()throwsSocketExceptionpublicvoidsetSoTimeout(inttimeout)throwsSocketException 这个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_SNDBUFpublicintgetSendBufferSize()throwsSocketExceptionpublicvoidsetSendBufferSize(intsize)throwsSocketException 在默认情况下,输出流的发送缓冲区是8096个字节(8K)。这个值是Java所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用setSendBufferSize方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。 如果底层的Socket实现不支持SO_SENDBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setSendBufferedSize方法将抛出IllegalArgumentException例外。 6. SO_RCVBUFpublicintgetReceiveBufferSize()throwsSocketExceptionpublicvoidsetReceiveBufferSize(intsize)throwsSocketException 在默认情况下,输入流的接收缓冲区是8096个字节(8K)。这个值是Java所建议的输入缓冲区的大小。如果这个默认值不能满足要求,可以用setReceiveBufferSize方法来重新设置缓冲区的大小。但最好不要将输入缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。 如果底层的Socket实现不支持SO_RCVBUF选项,这两个方法将会抛出SocketException例外。必须将size设为正整数,否则setReceiveBufferSize方法将抛出IllegalArgumentException例外。7. SO_KEEPALIVEpublicbooleangetKeepAlive()throwsSocketExceptionpublicvoidsetKeepAlive(booleanon)throwsSocketException 如果将这个Socket选项打开,客户端Socket每隔段的时间(大约两个小时)就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,在大约11分钟后,客户端Socket再发送一个数据包,如果在12分钟内,服务器还没响应,那么客户端Socket将关闭。如果将Socket选项关闭,客户端Socket在服务器无效的情况下可能会长时间不会关闭。SO_KEEPALIVE选项在默认情况下是关闭的,可以使用如下的语句将这个SO_KEEPALIVE选项打开:socket1.setKeepAlive(true); 8. SO_OOBINLINEpublicbooleangetOOBInline()throwsSocketExceptionpublicvoidsetOOBInline(booleanon)throwsSocketException 如果这个Socket选项打开,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,而是立即发出。虽然在客户端并不是使用OutputStream向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服务端程序中并不知道由客户端发过来的数据是由OutputStream还是由sendUrgentData发过来的。下面是sendUrgentData方法的声明:publicvoidsendUrgentData(intdata)throwsIOException 虽然sendUrgentData的参数data是int类型,但只有这个int类型的低字节被发送,其它的三个字节被忽略。下面的代码演示了如何使用SO_OOBINLINE选项来发送单字节数据。package mynet;.*;importjava.io.*;class Serverpublicstaticvoidmain(Stringargs)throwsExceptionServerSocketserverSocket=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 Clientpublicstaticvoidmain(Stringargs)throwsExceptionSocketsocket=newSocket(,1234);socket.setOOBInline(true);OutputStreamout=socket.getOutputStream();OutputStreamWriteroutWriter=newOutputStreamWriter(out);outWriter.write(67);/向服务器发送字符CoutWriter.write(helloworldrn);socket.sendUrgentData(65);/向服务器发送字符Asocket.sendUrgentData(322);/向服务器发送字符BoutWriter.flush();socket.sendUrgentData(214);/向服务器发送汉字”中”socket.sendUrgentData(208);socket.sendUrgentData(185);/向服务器发送汉字”国”socket.sendUrgentData(250);socket.close(); 由于运行上面的代码需要一个服务器类,因此,在加了一个类名为Server的服务器类,关于服务端套接字的使用方法将会在后面的文章中详细讨论。在类Server类中只使用了ServerSocket类的accept方法接收客户端的请求。并从客户端传来的数据中读取两行字符串,并显示在控制台上。测试 由于本例使用了,因Server和Client类必须在同一台机器上运行。 运行Serverjavamynet.Server 运行Clientjavamynet.Client 在服务端控制台的输出结果服务器已经启动,端口号:1234ABChelloworld中国 在ClienT类中使用了sendUrgentData方法向服务器发送了字符A(65)和B(66)。但发送B时实际发送的是322,由于sendUrgentData只发送整型数的低字节。因此,实际发送的是66.十进制整型322的二进制形式如图1所示。图1 十进制整型322的二进制形式 从图1可以看出,虽然322分布在了两个字节上,但它的低字节仍然是66. 在Client类中使用flush将缓冲区中的数据发送到服务器。我们可以从输出结果发现一个问题,在Client类中先后向服务器发送了C、hello worldrn、A、B.而在服务端程序的控制台上显示的却是ABChello world.这种现象说明使用sendUrgentData方法发送数据后,系统会立即将这些数据发送出去;而使用write发送数据,必须要使用flush方法才会真正发送数据。 在Client类中向服务器发送中国字符串。由于中是由214和208两个字节组成的;而国是由185和250两个字节组成的;因此,可分别发送这四个字节来传送中国字符串。 注意:在使用setOOBInline方法打开SO_OOBINLINE选项时要注意是必须在客户端和服务端程序同时使用setOOBInline方法打开这个选项,否则无法命名用sendUrgentData来发送数据。五、设置Socket的选项Socket有以下几个选项: n TCP_NODELAY:表示立即发送数据。 n SO_RESUSEADDR:表示是否允许重用Socket所绑定的本地地址。 n SO_TIMEOUT:表示接收数据时的等待超时时间。 n SO_LINGER:表示当执行Socket的close()方法时,是否立即关闭底层的Socket。 n SO_SNFBUF:表示发送数据的缓冲区的大小。 n SO_RCVBUF:表示接收数据的缓冲区的大小。 n SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。 n OOBINLINE:表示是否支持发送一个字节的TCP紧急数据。 1. TCP_NODELAY选项 1) 设置该选项:public void setTcpNoDelay(boolean on) throws SocketException 2) 读取该选项:public boolean getTcpNoDelay() throws SocketException 3) TCP_NODEALY的默认值为false,表示采用Negale算法。如果调用setTcpNoDelay(true)方法,就会关闭Socket的缓冲,确保数据及时发送:if(!socket.getTcpNoDelay() socket.setTcpNoDelay(true); 4) 如果Socket的底层实现不支持TCP_NODELAY选项,那么getTcpNoDelay()和setTcpNoDelay()方法会抛出SocketException。2. SO_RESUSEADDR选项 1) 设置该选项:public void setResuseAddress(boolean on) throws SocketException 2) 读取该选项:public boolean getResuseAddress() throws SocketException 3) 为了确保一个进程关闭了Socket后,即使它还没释放端口,同一个主机上的其他进程还可以立刻重用该端口,可以调用Socket的setResuseAddress(true)方法:if(!socket.getResuseAddress() socket.setResuseAddress(true);4) 值得注意的是socket.setResuseAddress(true)方法必须在Socket还没有绑定到一个本地端口之前调用,否则执行socket.setResuseAddress(true)方法无效。因此必须按照以下方式创建Socket对象,然后再连接远程服务器:Socket socket = new Socket(); /此时Socket对象未绑定到本地端口,并且未连接远程服务器socket.setResuseAddress(true); SocketAddress remoteAddr = new InetSocketAddress(remotehost,8000);socket.connect(remoteAddr); /连接远程服务器,并且绑定匿名的本地端口 或者: Socket socket = new Socket(); /此时Socket对象未绑定到本地端口,并且未连接远程服务器socket.setResuseAddress(true); SocketAddress localAddr = new InetSocketAddress(localhost,9000);SocketAddress remoteAddr = new InetSocketAddress(remotehost,8000);socket.bind(localAddr); /与本地端口绑定 socket.connect(remoteAddr); /连接远程服务器,并且绑定匿名的本地端口3. SO_TIMEOUT选项 1) 设置该选项:public void setSoTimeout(int milliseconds) throws SocketException 2) 读取该选项:public int getSoTimeOut() throws SocketException 3) 当通过Socket的输入流读数据时,如果还没有数据,就会等待。Socket类的SO_TIMEOUT选项用于设定接收数据的等待超时时间,单位为毫秒,它的默认值为0,表示会无限等待,永远不会超时。4) Socket的setSoTimeout()方法必须在接收数据之前执行才有效。此外,当输入流的read()方法抛出SocketTimeoutException后,Socket仍然是连接的,可以尝试再次读取数据。4. SO_LINGER选项 1) 设置该选项:public void setSoLinger(boolean on, int seconds) throws SocketException2) 读取该选项:public int getSoLinger() throws SocketException 3) SO_LINGER选项用来控制Socket关闭时的行为。 l socket.setSoLinger(true,0):执行Socket的close()方法时,该方法也会立即返回,但底层的Socket也会立即关闭,所有未发送完的剩余数据被丢弃。l socket.setSoLinger(true,3600):执行Socket的close()方法时,该方法不会立即返回,而进入阻塞状态,同时,底层的Socket会尝试发送剩余的数据。只有满足以下两个条件之一,close()方法才返回:n 底层的Socket已经发送完所有的剩余数据。 n 尽管底层的Socket还没有发送完所有的剩余数据,但已经阻塞了3600秒。close()方法的阻塞时间超过3600秒,也会返回,剩余未发送的数据被丢弃。以上两种情况内,当close()方法返回后,底层的Socket会被关闭,断开连接。 4) setSoLinger(boolean on ,int second)方法中的seconds参数以秒为单位,而不是以毫秒为单位。 5. SO_RCVBUF选项 1) 设置该选项:public void setReceiveBufferSize(int size) throws SocketException 2) 读取该选项:public int getReceiveBufferSize() throws SocketException 3) SO_RCVBUF表示Socket的用于输入数据的缓冲区的大小。 4) 如果底层Socket不支持SO_RCVBUF选项,那么setReceiveBufferSize()方法会抛出SocketException。 6. SO_SNDBUF选项 1) 设置该选项:public void setSendBufferSize(int size) throws SocketException 2) 读取该选项:public int getSendBufferSize() throws SocketException 3) SO_SNDBUF表示Socket的用于输出数据的缓冲区的大小。 4) 如果底层Socket不支持SO_SNDBUF选项,setSendBufferSize()方法会抛出SocketException。 7. SO_KEEPALIVE选项 1) 设置该选项:public void setKeepAlive(boolean on) throws SocketException 2) 读取该选项:public int getKeepAlive() throws SocketException 3) 当SO_KEEPALIVE选项为true,表示底层的TCP实现会监视该连接是否有效。 4) SO_KEEPALIVE选项的默认值为false,表示TCP不会监视连接是否有效,不活动的客户端可能会永久存在下去,而不会注意到服务器已经崩溃。 8. OOBINLINE选项 1) 设置该选项:public void setOOBInline(int size) throws SocketException 2) 读取该选项:public int getO

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论