`

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

阅读更多
ServerSocket类有以下三个选项:
1.       SO_TIMEOUT 设置accept方法的超时时间。
2.       SO_REUSEADDR设置服务端同一个端口是否可以多次绑定。
3.       SO_RECBUF设置接收缓冲区的大小。
一、SO_TIMEOUT选项
可以通过SeverSocket类的两个方法(setSoTimeoutgetSoTimeout)来设置和获得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));

st1":*{behavior:url(#ieooui) }

二、SO_REUSEADDR选项
SO_REUSEADDR选项决定了一个端口是否可以被绑定多次。可以通过SeverSocket类的两个方法(setReuseAddresgetReuseAddress)来设置和获得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)。而serverSocket2serverSocket3并不是第一次绑定端口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()去掉,再执行上面操作步骤,看看会有什么结果。

st1":*{behavior:url(#ieooui) } 三、SO_RCVBUF选项
可以通过SeverSocket类的两个方法(setReceiveBufferSizegetReceiveBufferSize)来设置和获得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));
st1":*{behavior:url(#ieooui) }
   一般情况下,并不需要设置这个选项,它的默认值(一般为8K)足可以满足大多数情况。但有时为了适应特殊的需要,必须更改接收缓冲区的值。如在一些网络游戏中,需要实时地向服务器传送各种动作、指令信息。这就需要将接收缓冲区设小一点。这样可以在一定程度上增加游戏客户端的灵敏度。如果需要传送大量的数据,如HTTPFTP等协议。这就需要较大的接收缓冲区。

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

相关推荐

    Java网络编程从入门到精通

    本教程"Java网络编程从入门到精通"旨在帮助初学者和有经验的开发者深入理解这一领域,从基础知识到高级主题,提供了一套全面的学习资源。 在Java中,网络编程主要涉及以下核心知识点: 1. **Socket编程**:Java的...

    C#socket编程从入门到精通

    在探讨C# socket编程的过程中,我们首先需要了解的是如何操作IP地址,这是进行网络通信的基础。C#中的IPAddress类提供了方便的方法...对于希望从入门到精通C# socket编程的读者而言,这些内容是必须掌握的核心知识点。

    JAVA网络编程从入门到精通

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

    C_socket编程从入门到精通

    ### C_socket编程从入门到精通 #### 一、基础知识概览 在开始深入探讨C_socket编程之前,我们需要先了解一些基础概念和相关的类与方法。本文将重点介绍以下几个方面: 1. **IP地址操作类**:包括`IPAddress`类...

    Java基于socket实现的客户端和服务端通信功能完整实例

    DataOutputStream用来将数据写入到Socket的输出流中,DataInputStream用来从Socket的输入流中读取数据。 总结 本文详细介绍了Java基于Socket实现的客户端和服务端通信功能,包括客户端和服务器端的实现、连接和...

    java网络编程客户端和服务端

    Java网络编程是构建分布式系统的关键技术之一,尤其对于Java初学者来说,理解并掌握客户端和服务端的交互原理至关重要。在本教程中,我们将探讨如何使用Java的`ServerSocket`和`Socket`类来实现基本的网络通信。 ...

    java socket网络编程代码(服务端和客户端)

    1. **Java Socket**:Java Socket API是Java提供的用于进行网络通信的基础组件,支持TCP/IP协议,可以创建双向通信通道。 2. **网络编程**:网络编程是指编写能够通过网络进行通信的程序,涉及网络协议、数据传输、...

    网络实验编程:TCP客户端与服务端Socket通信 带GUI

    本实验主题为“网络实验编程:TCP客户端与服务端Socket通信 带GUI”,主要涉及Java语言、TCP协议、Socket编程以及Swing图形用户界面(GUI)设计。下面将详细阐述这些知识点。 首先,Java是一种广泛使用的面向对象的...

    网络编程 socket 编程 用户登录注册 服务端与客户端

    Socket编程是网络编程的基础,它提供了一种在不同计算机之间建立连接并交换数据的方法。在这个“网络编程 Socket 编程 用户登录注册 服务端与客户端”的项目中,我们将会深入探讨如何使用Socket实现一个完整的用户...

    java网络编程服务端

    java网络编程:控制台输入,服务端代码!

    socket网络编程实例代码_socket服务端编程实例代码_

    总的来说,Socket网络编程是构建网络应用程序的基础,理解并掌握服务端和客户端的实现细节,对于开发网络应用至关重要。通过阅读和实践`server.c.txt`和`client.c.txt`,开发者能够深入理解Socket编程的基本原理和...

    Linux多线程服务端编程:使用muduo+C网络库

    Linux多线程服务端编程:使用muduo+C网络库.pdf Linux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:使用muduo+C网络库.pdfLinux多线程服务端编程:...

    Java从入门到精通(第四版)官方程序实例

    《Java从入门到精通(第四版)》是一本旨在帮助初学者和有一定基础的程序员深入理解Java编程语言的书籍。本书以丰富的实例为引导,旨在让读者通过实践掌握Java的各项核心概念和技术。配套的源代码提供了大量实例,...

    java socket 编程文档

    2. 服务端:监听特定端口,接受客户端连接,创建Socket。 3. 双方:通过Socket的输入输出流进行数据交换。 4. 结束:当通信完成后,双方关闭Socket和相关流。 四、异常处理 在进行Socket编程时,需要处理各种可能的...

    自己写的Java Socket服务端

    Java Socket服务端是一种基于TCP/IP协议的网络通信方式,它允许服务器端与多个客户端进行双向通信。在Java中,Socket编程提供了低级别的、基于字节流的通信原语,是构建分布式应用程序的基础。本项目是一个自己编写...

    MFC:Socket编程—TCP服务端和多个客户端通信 示例代码

    在本文中,我们将深入探讨MFC(Microsoft Foundation Classes)中的Socket编程,特别是如何实现一个TCP服务端与多个客户端之间的通信。MFC是微软提供的一套C++类库,用于简化Windows应用程序开发,其中包括对网络...

    java网络编程源码

    1. **Java Socket编程**:Java通过Socket类实现了TCP/IP通信,Socket是网络通信的基本单元,用于建立客户端和服务器端的连接。在源码中,你会看到如何创建ServerSocket监听客户端连接,以及如何用Socket建立客户端...

    JAVA编程实现基于Socket通信(服务端、客户端简单对话)

    此代码通过Java编程实现了基于Socket的网络通信,通信时有两个界面,分别是服务端、客户端,可发消息,界面上有显示,默认是本机的IP地址,代码可用JCreator直接打开,文件是.java形式。

Global site tag (gtag.js) - Google Analytics