Java套接字Socket编程--TCP参数

简介: 在Java的Socket中,主要包含了以下可设置的TCP参数。 属性 说明 默认值 SO_TIMEOUT 对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长空等待时间。

在Java的Socket中,主要包含了以下可设置的TCP参数。

属性

说明

默认值

SO_TIMEOUT

对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长空等待时间。

0

TCP_NODELAY

是否一有数据就马上发送。

false

SO_LINGER

优雅地关闭套接字,或者立刻关闭。

-1

SO_SNDBUF

发送数据的缓冲区大小。

8K

SO_RCVBUF

接收数据的缓冲区大小。

8K

SO_KEEPALIVE

是否启用心跳机制。

false

SO_REUSEADDR

是否地址重用。

false

BACKLOG

服务端处理线程全忙后,允许多少个新请求进入等待。

50

 

1.1 BACKLOG

BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。

ServerSocket serverSocket = new ServerSocket(8080, 100);       

 

1.2 TCP_NODELAY

在TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。这里就涉及到一个名为Nagle的算法,该算法的目的就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。

TCP_NODELAY选项,就是用于启用或关于Nagle算法。如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true关闭Nagle算法;如果要减少发送次数减少网络交互,就设置为false等累积一定大小后再发送。默认为false。

Socket中操作该属性的方法如下:

void setTcpNoDelay(boolean on)

          启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。          

boolean getTcpNoDelay()

          测试是否启用 TCP_NODELAY。

 

关于Nagle算法介绍,请参考附录部分。

 

1.3 SO_TIMEOUT

对于服务端套接字ServerSocket来说,SO_TIMEOUT表示服务端accept方法空等待客户端连接的最长时间;对于客户端套接字Socket来说,SO_TIMEOUT表示输入流读取数据read方法的最长等待时间。一旦超过设置的SO_TIMEOUT,程度将抛出超时异常。

ServerSocket/Socket中操作该属性的方法如下:

int getSoTimeout()

          返回 SO_TIMEOUT 的设置。

void setSoTimeout(int timeout)

          启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。

 

使用示例:

ServerSocket serverSocket = new ServerSocket(8080);       

serverSocket.setSoTimeout(30000);

 

Socket clientSocket = serverSocket.accept();

clientSocket.setSoTimeout(20000);

 

1.4 SO_LINGER

当调用closesocket关闭套接字时,SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据。处理方式无非两种:丢弃或者将数据继续发送至对端,优雅关闭连接。事实上,SO_LINGER并不被推荐使用,大多数情况下我们推荐使用默认的关闭方式(即下方表格中的第一种情况)。

下方代码段显示linger结构语法,表格为不同参数情况下的套接字行为。

typedef struct linger { 

  u_short l_onoff;    //开关,零或者非零 

  u_short l_linger;   //优雅关闭最长时限 

} linger;

 

各字段与对应行为如下表所示。

l_onoff

l_linger

closesocket行为

发送队列

底层行为

忽略

立即返回。

保持直至发送完成。

系统接管套接字并保证将数据发送至对端。

非零

立即返回。

立即放弃。

直接发送RST包,自身立即复位,不用经过2MSL状态。对端收到复位错误号。

非零

非零

阻塞直到l_linger时间超时或数据发送完成。(套接字必须设置为阻塞)

在超时时间段内保持尝试发送,若超时则立即放弃。

超时则同第二种情况,若发送完成则皆大欢喜。

 

Socket中操作该属性的方法如下:

void setSoLinger(boolean on, int linger)

          启用/禁用具有指定逗留时间(以秒为单位)的SO_LINGER。 Linger最大取值为65535。

int getSoLinger()

          返回 SO_LINGER 的设置。默认值为-1。

 

由于getSoLinger()方法返回的-1没有太多意思,我们查看到Java的默认实现PlainSocketImpl.c文件中,赋值操作代码片段如下所示。

/*

 * Class:     java_net_PlainSocketImpl

 * Method:    socketSetOption

 * Signature: (IZLjava/lang/Object;)V

 */

JNIEXPORT void JNICALL

Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this,

                                              jint cmd, jboolean on,

                                              jobject value) {

    …

    switch (cmd) {

        case java_net_SocketOptions_SO_SNDBUF :

        case java_net_SocketOptions_SO_RCVBUF :

        case java_net_SocketOptions_SO_LINGER :

        case java_net_SocketOptions_IP_TOS :

            {

                …

                if (cmd == java_net_SocketOptions_SO_LINGER) {

                    if (on) {

                        optval.ling.l_onoff = 1;

                        optval.ling.l_linger = (*env)->GetIntField(env, value, fid);

                    } else {

                        optval.ling.l_onoff = 0;

                        optval.ling.l_linger = 0;

                    }

                    optlen = sizeof(optval.ling);

                } else {

                    optval.i = (*env)->GetIntField(env, value, fid);

                    optlen = sizeof(optval.i);

                }

 

                break;

            }

 

        /* Boolean -> int */

        default :

            optval.i = (on ? 1 : 0);

            optlen = sizeof(optval.i);

 

    }

}

从蓝色字体部分代码可以看出,只要赋值为false,则底层linger结构中的l_onoff和l_linger的值均为0,符合表中的第一种情况。

 

1.5 SO_SNDBUF

发送缓冲区的大小设置,默认为8K。

Socket中操作该属性的方法如下:

void setSendBufferSize(int size)

     将此 Socket 的 SO_SNDBUF 选项设置为指定的值。

int getSendBufferSize()

     获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。

 

1.6 SO_RCVBUF

接收缓冲区大小设置,默认为8K。该属性既可以在ServerSocket实例中设置,也可以在Socket实例中设置。

ServerSocket/Socket中操作该属性的方法如下:

void setReceiveBufferSize(int size)

     将此 Socket 的 SO_RCVBUF 选项设置为指定的值。          

int getReceiveBufferSize()

     获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。

 

1.7 SO_KEEPALIVE

套接字本身是有一套心跳保活机制的,不过默认的设置并不像我们一厢情愿的那样有效。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。

很多人认为两个小时的时间设置得很不合理。为什么不设置成为10分钟,或者更短的时间?(可以通过SO_KEEPALIVE选项设置。)但是这样做其实并不被推荐。实际上这套机制只是操作系统底层使用的一个被动机制,原理上不应该被上层应用层使用。当系统关闭一个由KEEPALIVE机制检查出来的死连接时,是不会主动通知上层应用的,只有在调用相应的IO操作在返回值中检查出来。

在《UNIX网络编程第1卷》中也有详细的阐述:

SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况:对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接口本身则被关闭。对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟 15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。

因此,忘记SO_KEEPALIVE,在应用层自己写一套保活机制比较靠谱。

Socket中操作该属性的方法如下:

boolean getKeepAlive()

      测试是否启用 SO_KEEPALIVE。

void setKeepAlive(boolean on)

      启用/禁用 SO_KEEPALIVE。

 

1.8 SO_REUSEADDR

    是否重用处于TIME_WAIT状态的地址。默认为false。

Socket中操作该属性的方法如下:

boolean getReuseAddress()

      测试是否启用 SO_REUSEADDR。

void setReuseAddress(boolean on)

      启用/禁用 SO_REUSEADDR 套接字选项。

作者:Bonker
出处:http://www.cnblogs.com/Bonker
QQ:519841366
       
本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利
目录
相关文章
|
8天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
8天前
|
程序员 开发者 Python
Python网络编程基础(Socket编程) 错误处理和异常处理的最佳实践
【4月更文挑战第11天】在网络编程中,错误处理和异常管理不仅是为了程序的健壮性,也是为了提供清晰的用户反馈以及优雅的故障恢复。在前面的章节中,我们讨论了如何使用`try-except`语句来处理网络错误。现在,我们将深入探讨错误处理和异常处理的最佳实践。
|
10天前
|
安全 Java 开发者
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第9天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将详细解析Java中的同步机制,包括synchronized关键字、Lock接口以及并发集合等,并探讨它们如何影响程序的性能。此外,我们还将讨论Java内存模型,以及它如何影响并发程序的行为。最后,我们将提供一些实用的并发编程技巧和最佳实践,帮助开发者编写出既线程安全又高效的Java程序。
22 3
|
13天前
|
网络协议 程序员 Python
pythonTCP客户端编程创建Socket对象
【4月更文挑战第6天】本教程介绍了TCP客户端如何创建Socket对象。Socket作为网络通信的基础单元,包含协议、IP地址和端口等信息。在TCP/IP中,Socket分为流式(TCP)、数据报(UDP)和原始套接字。以Python为例,创建TCP Socket对象需调用`socket.socket(AF_INET, SOCK_STREAM)`。为确保健壮性,应使用异常处理处理可能的`socket.error`。学习本教程将帮助你掌握TCP客户端创建Socket对象的技能。
|
11天前
|
Java
Java 并发编程:深入理解线程池
【4月更文挑战第8天】本文将深入探讨 Java 中的线程池技术,包括其工作原理、优势以及如何使用。线程池是 Java 并发编程的重要工具,它可以有效地管理和控制线程的执行,提高系统性能。通过本文的学习,读者将对线程池有更深入的理解,并能在实际开发中灵活运用。
|
8天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
12天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第7天】在现代软件开发中,多线程编程已经成为一种不可或缺的技术。为了提高程序性能和资源利用率,Java提供了线程池这一强大工具。本文将深入探讨Java线程池的原理、使用方法以及如何根据实际需求定制线程池,帮助读者更好地理解和应用线程池技术。
15 0
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
2天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
3天前
|
缓存 监控 Java
Java并发编程:线程池与任务调度
【4月更文挑战第16天】Java并发编程中,线程池和任务调度是核心概念,能提升系统性能和响应速度。线程池通过重用线程减少创建销毁开销,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。任务调度允许立即或延迟执行任务,具有灵活性。最佳实践包括合理配置线程池大小、避免过度使用线程、及时关闭线程池和处理异常。掌握这些能有效管理并发任务,避免性能瓶颈。