在网络编程中,Socket/ServerSocket有一些选项用来自定义一些行为,现在分享一下。
 

 

Socket选项

1.TCP_NODELAY

在Socket发送数据时,默认情况下,数据会先进入缓冲区,等缓冲区满了再发送出去,意图是为了通过减少传输数据的次数,以此来提高通信效率

但是,对于一些需要即时发送,即时响应的场景并不合适,比如网络游戏。客户端因为采用的默认行为,会等到缓冲区满了后才发送数据,服务端的响应相应也会变慢,导致整个游戏运行起来不流畅。

这时,就要开启TCP_NODELAY,关闭默认行为,实时发送实时响应。

 

2.SO_TIMEOUT

在读取数据时,常常因为种种原因,导致读方法阻塞,为了保证阻塞时间可控,可以设置SO_TIMEOUT选项,设置读数据时的最大等待时间,如果阻塞时间超过设置的时间,

则会抛出【Exception in thread "main" java.net.SocketTimeoutException: Read timed out】。示例如下。

客户端代码:阻塞6000ms后才发送数据

public class Main {

    public static void main(String[] args) throws InterruptedException, IOException {

        Socket socket = new Socket("127.0.0.1", 8899);
System.out.println("客户端启动...");
OutputStream out = socket.getOutputStream();
Thread.sleep(6000); //阻塞6000ms后才发送数据
out.write("This is for everyone".getBytes());
}
}

服务端代码:通过SO_TIMEOUT选项,设置读阻塞时长最大为5000ms,由于客户端再6000ms后才会出数据,所以会抛出SocketTimeoutException异常。

public class SimpleServer {

    public static void main(String[] args) throws IOException, InterruptedException {

        ServerSocket serverSocket = new ServerSocket(8899, 2);
System.out.println("服务端启动...");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("连接成功:" + socket);
soTimeout(socket);
}
} private static void soTimeout(Socket socket) throws IOException {
socket.setSoTimeout(5000); //设置最大阻塞时间为5000ms
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) >= 0) { //因为阻塞时间超过5000ms,所以这里抛出【Exception in thread "main" java.net.SocketTimeoutException: Read timed out】异常
System.out.println("read,len=" + len + ",str=" + new String(buf, 0, len));
}
}
}

3.SO_REUSEADDR

对于Socket来说,当通过Socket.close()关闭时,底层的Socket为了把数据发送完或者接受完,不会立即关闭而是会等待一段时间,确保完成自己的使命。

但这会带来一个问题,由于Socket的端口号不能共享,一旦一个端口号被占用,之后再用这个端口号尝试建立新连接的话会报端口冲突的异常。

为了确保一个进程关闭Socket后,同一主机的其他进程还能使用这个端口,可以设置SO_REUSEADDR选项。

4.SO_LINGER

SO_LINGER选项用来控制Socket关闭时的行为,上面已经简单讲过,默认情况下,调用Socket.close()方法后,close()方法会立即返回,但是底层的Socket会等待数据发送完成后再关闭。

当设置Socket.setSoLinger(true, 0)选项时,close()方法也会立即返回,底层的Socket也会立即关闭,未发送完的数据会抛弃掉。

当设置Socket.setSoLinger(true, n)选项时,close()方法会阻塞,直到满足下列条件中的一个才会返回。

  1. n秒后,即使底层Socket还未发送完,也强制关闭底层Socket,并返回close()方法。

  2. 底层Socket数据全部发送完(全部送到缓冲区)。

示例如下。

服务端代码:没什么特别的,就是从Socket中读出数据,重点在客户端

public class SimpleServer {

    public static void main(String[] args) throws IOException, InterruptedException {

        ServerSocket serverSocket = new ServerSocket(8899, 2);
System.out.println("服务端启动...");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("连接成功:" + socket);
soLinger(socket);
}
} // 没什么特别的,就是从Socket中读出数据,重点在客户端
private static void soLinger(Socket socket) throws InterruptedException, IOException { InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) >= 0) {
System.out.println("read,len=" + len + ",str=" + new String(buf, 0, len));
}
}
}

客户端代码:首先是不设置SO_LINGER选项,已默认的方式运行。之后#1,close()方法立即返回。#2,close()方法阻塞了4ms。

public class Main {

    public static void main(String[] args) throws InterruptedException, IOException {

        Socket socket = new Socket("127.0.0.1", 8899);
//socket.setSoLinger(true, 0); #1
//socket.setSoLinger(true, 10); #2
System.out.println("客户端启动...");
OutputStream out = socket.getOutputStream();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 500000; i++) {
sb.append(i);
}
out.write(sb.toString().getBytes());
long start = System.currentTimeMillis();
socket.close();
long end = System.currentTimeMillis();
System.out.println("close:" + (end - start)); //#1:0 #2:4
}
}

默认情况下,close()方法立即返回,Socket底层在发送完数据后关闭,服务端正常运行。

当把#1的注释给删掉,close()方法立即返回,Socket底层也会立即关闭,因为客户端数据没有完全发出,所以服务端运行时抛出了【Exception in thread "main" java.net.SocketException: Connection reset】异常

当吧#2的注释给删掉,close()方法阻塞了4ms才返回,Socket底层在发送完数据后关闭,服务端正常运行。

5.SO_RCVBUF,SO_SNDBUF

设置输入,输出缓冲区的大小。

一般情况下,对于数据量大,传输频率低的场景,适合大的缓冲区,减少传输数据的次数,提高传输效率,比如FTP、HTTP等。

对于数据量小,传输频率高,对实时性要求高的场景,适合设置小的缓冲区。

6.SO_KEEPALIVE

检测TCP连接的有效性。大概机制是,2小时后双方没有过交互一直处于空闲状态,则会发送一个请求,如果请求没有响应,则会认为对方已经关闭,这样本地的Socket也会自动关闭。

ServerSocket选项

1.SO_TIMEOUT

ServerSocket.accept()方法是个阻塞方法,会一直阻塞到有一个TCP连接成功建立,SO_TIMEOUT选项则可以设置最大的阻塞时间,超过这个时间还没有建立TCP连接的话则抛出异常。

注意和Socket的SO_TIMEOUT选项区别开,Socket的SO_TIMEOUT选项是设置读操作的最大阻塞时间,ServerSocket的SO_TIMEOUT选项是设置accept()操作的最大阻塞时间。

服务端代码:5000ms后任然没有与某个客户端成功建立连接,所以会抛出SocketTimeoutException异常。

public class SimpleServer {

    public static void main(String[] args) throws IOException, InterruptedException {

        ServerSocket serverSocket = new ServerSocket(8899, 2);
serverSocket.setSoTimeout(5000);
System.out.println("服务端启动...");
while (true) {
Socket socket = serverSocket.accept(); ////SO_TIMEOUT选项为5000ms,超过这段时间后,抛出【Exception in thread "main" java.net.SocketTimeoutException: Accept timed out】
System.out.println("连接成功:" + socket);
}
}
}

2.SO_REUSEADDR

同Socket的SO_REUSEADDR选项类似,为了保证一个进程关闭ServerSocket后,即使它还没有释放端口,同一主机的其他进程还能使用这个端口,可以设置SO_REUSEADDR解决这个问题。

对于Socket来讲,一般Socket的端口号都是系统随机分配的,碰巧碰到同一端口的情况比较少。但是对于ServerSocket来讲,往往用的都是固定端口号,所以同一端口的情况就不能忽视了。

3.SO_RCVBUF

设置ServerSocket输入缓冲区的大小。

引用

1.《Java网络编程精解》(孙卫琴)

Socket/ServerSocket 选项的更多相关文章

  1. 简单通过java的socket&serversocket以及多线程技术实现多客户端的数据的传输,并将数据写入hbase中

    业务需求说明,由于公司数据中心处于刚开始部署的阶段,这需要涉及其它部分将数据全部汇总到数据中心,这实现的方式是同上传json文件,通过采用socket&serversocket实现传输. 其中 ...

  2. 网络编程之Socket & ServerSocket

    网络编程之Socket & ServerSocket Socket:网络套接字,网络插座,建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP ...

  3. 随笔 -- IO -- Socket/ServerSocket -- 系统概述

    随笔 -- IO -- Socket/ServerSocket -- Echo(BIO)实例 Java 网络编程 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java ...

  4. 随笔 -- IO -- Socket/ServerSocket -- Echo(BIO)实例

    随笔 -- IO -- Socket/ServerSocket -- 系统概述 Java中提供的专门的网络开发程序包------java.net Java的网络编程提供的两种通信协议:TCP和UDP ...

  5. java之TCP(Socket,serverSocket)实例

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...

  6. SOCKET:SO_LINGER 选项

    好多次接触到SO_LINGER选项,但总是忘了这是干什么用的.现在整理一下,我才明白这个参数是用来设定“SOCKET在CLOSE时候是否等待缓冲区发送完成”这个特性的.下面是一些详细的说明. sets ...

  7. socket系列之服务器端socket——ServerSocket类

    一般地,Socket可分为TCP套接字和UDP套接字,再进一步,还可以被分为服务器端套接字跟客户端套接字.这节我们先关注TCP套接字的服务器端socket,Java中ServerSocket类与之相对 ...

  8. JAVA-7NIO之Socket/ServerSocket Channel

    一.ServerSocketChannel Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样.Ser ...

  9. Java (Socket,ServerSocket)与(SocketChannel,ServerSocketChannel)区别和联系

    Socket 和ServerSocke 是一对 他们是java.net下面实现socket通信的类SocketChannel 和ServerSocketChannel是一对 他们是java.nio下面 ...

随机推荐

  1. Confluence 6 关于嵌入的 H2 数据库

    你的 Confluence 安装中包含有嵌入的 H2 数据库,能够让你试用 Confluence 而不需要安装任何的外部数据库.H2 数据库仅仅用于你对 Confluence 进行评估.在你将 Con ...

  2. Confluence 6 修改站点图标(favicon)

    你也可以修改你站点的图标(这个站点图标将会在你浏览器的标签页上显示).你需要 Confluence 的管理员权限才能进行修改. 进入  > 基本配置(General Configuration) ...

  3. deepin、Ubuntu安装Nginx

    deepin安装nginx 切换至root用户 su 密码: 基础库的安装 安装gcc g++的依赖库 sudo apt-get install build-essential && ...

  4. xilinx_all_version.lic

    INCREMENT ISE_Vivado_Seth xilinxd -dec- uncounted \ C25FB036D304 VENDOR_STRING=License_Type:Bought H ...

  5. laravel 注入那点事

    public function delete(Group $groupId, Post $postId) { $postId->delete(); return response()->j ...

  6. bzoj2200拓扑排序+最短路+联通块

    自己写的不知道哪里wa了,明明和网上的代码差不多.,. /* 给定一张图,有的边是无向边,有的是有向边,有向边不会出现在环中,且有可能是负权值 现在给定起点s,求出s到其余所有点的最短路长度 任何存在 ...

  7. Nginx详解二十二:Nginx深度学习篇之Lua解释器安装及基础语法

    解释器 Lua:Lua是一个简洁.轻量.可扩展的脚本语言 Nginx+Lua优势充分的结合Nginx的并发处理epoll优势的Lua的轻量实现简单的功能切高并发的场景 安装Lua 1.安装解释器:yu ...

  8. JS去除空格和换行的正则表达式(推荐)

    //去除空格  String.prototype.Trim = function() {  return this.replace(/\s+/g, "");  }    //去除换 ...

  9. java 基础知识点必备

    1.为什么集合类没有实现Cloneable和Serializable接口? 克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的.因此,应该由集合类的具体实 ...

  10. C# 读取sqlite文件

    class Program { static void Main(string[] args) { getsqliteData(); } public static void getsqliteDat ...