• Java Socket Option


    选项

        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;

    一。TCP_NODELAY

     在默认false下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字

    节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包

    的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法

    二。SO_REUSEADDR

         如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 抛出“Address

    already in use: JVM_Bind”。如果你的服务程序停止后想立即重启,不等60秒,而新套接字依旧 使用同一端口,此时 SO_REUSEADDR 选项非常有用。

    在Windows平台,多个Socket新建立对象可以绑定在同一个端口上,这些新连接是非TIME_WAIT状态的。这样做并没有多大意义。

    在Linux平台,只有TCP状态位于 TIME_WAIT ,才可以重用 端口。这才是正确的行为。

    publicclass Test {
    
        public static void main(String[] args) {
    
            try {
    
                ServerSocket socket1 = new ServerSocket();
                ServerSocket socket2 = new ServerSocket();
                socket1.setReuseAddress(true);
                socket1.bind(new InetSocketAddress("127.0.0.1", 8899));
                System.out.println("socket1.getReuseAddress():" + socket1.getReuseAddress());
                socket2.setReuseAddress(true);
                socket2.bind(new InetSocketAddress("127.0.0.1", 8899));
                System.out.println("socket2.getReuseAddress():" + socket1.getReuseAddress());
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    使用SO_REUSEADDR选项时有两点需要注意:

        1.  必须在调用bind方法之前使用setReuseAddress方法来打开SO_REUSEADDR选项。因此,要想使用SO_REUSEADDR选项,就不能通过Socket

    类的构造方法来绑定端口。

        2.  必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR选项都打开才能起作用。socket1和socket2都使用了setReuseAddress方法打开

    了各自的SO_REUSEADDR选项。

    在Windows平台表现的特点是不正确的:

    socket1.getReuseAddress():true
    socket2.getReuseAddress():true

     在Linux平台表现的特点是正确的: 因为第一个连接不是TIME_WAIT状态的,第二个连接就不能使用8899端口

    socket1.getReuseAddress():true
    java.net.BindException: Address already in use
        at java.net.PlainSocketImpl.socketBind(Native Method)
        at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:383)
        at java.net.ServerSocket.bind(ServerSocket.java:328)
        at java.net.ServerSocket.bind(ServerSocket.java:286)
        at com.Test.main(Test.java:15)

    三。SO_LINGER

        这个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);

    四。SO_TIMEOUT

      可以通过这个选项来设置读取数据超时。当输入流的read方法被阻塞时,如果设置timeout,那么系统在等待了timeout毫秒后会抛出

    InterruptedIOException。在抛出后,输入流并未关闭,你可以继续通过read方法读取数据。

        如果将timeout设为0,就意味着read将会无限等待下去,直到服务端程序关闭这个Socket。这也是timeout的默认值。

    五。SO_SNDBUF

      在默认情况下,输出流的发送缓冲区是8096个字节(8K)。这个值是Java所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用

    setSendBufferSize方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。

    六。SO_RCVBUF

    The value of SO_RCVBUF is also used to set the TCP receive window that is advertized to the remote peer. Generally, the window size

    can be modified at any time when a socket is connected. However, if a receive window larger than 64K is required then this must be

    requested before the socket is connected to the remote peer. There are two cases to be aware of:

    1. For sockets accepted from a ServerSocket, this must be done by calling ServerSocket.setReceiveBufferSize(int) before the

    ServerSocket is bound to a local address. 

         2. For client sockets, setReceiveBufferSize() must be called before connecting the socket to its remote peer.

    七。SO_KEEPALIVE

      如果将这个选项打开,客户端Socket每隔段的时间就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检

    测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,那么客户端Socket将关闭。

      如果将这个选项关闭,客户端Socket在服务器无效的情况下可能会长时间不会关闭。

    八。SO_OOBINLINE

       如果这个Socket选项打开,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,

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

      虽然在客户端并不是使用OutputStream向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服

    务端程序中并不知道由客户端发过来的数据是由OutputStream还是由sendUrgentData发过来的。

      Note, only limited support is provided for handling incoming urgent data. In particular, no notification of incoming urgent data

    is provided and there is no capability to distinguish between normal data and urgent data unless provided by a higher level protocol.

    public 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
    ");
            socket.sendUrgentData(65);        // 向服务器发送字符"A"
            socket.sendUrgentData(322);        // 向服务器发送字符"B"
            outWriter.flush();
            socket.sendUrgentData(214);       // 向服务器发送汉字”中”
            socket.sendUrgentData(208);
            socket.sendUrgentData(185);       // 向服务器发送汉字”国”
            socket.sendUrgentData(250);
            socket.close();
        }
    }

    虽然sendUrgentData(int date)的参数data是int类型,但只有这个int类型的低字节被发送,其它的三个字节被忽略。

    sendUrgentData(322)时实际发送的是66,所以输出B

    服务器已经启动,端口号:1234
    ABChello world
    中国

    输出结果说明使用sendUrgentData方法发送数据后,系统会立即将这些数据发送出去;而使用write发送数据,必须要使用flush方法才会真正发送数据。

  • 相关阅读:
    文件读写和进度条
    复选框选择变化(可以演化成简单的字符串拼接)
    读取文本方式的简单登录
    计算字符出现次数
    判断系统版本号
    DataTable合并
    获取单元格值的数据类型
    struts2 日期标签
    jsp获取枚举的值
    java web项目修改项目名称
  • 原文地址:https://www.cnblogs.com/yuyutianxia/p/4077717.html
Copyright © 2020-2023  润新知