在网络编程中,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 导入模板的步骤

    第一步:检查你 Confluence 站点中安装的模板组件 查看当前已经导入到你 Confluence 站点中可用的模板组件: 以系统管理员或者 Confluence 管理员权限登录 Confluen ...

  2. pytorch的学习资源

    安装:https://github.com/pytorch/pytorch 文档:http://pytorch.org/tutorials/beginner/blitz/tensor_tutorial ...

  3. day 13 装饰器

    装饰器基础 装饰器的目的是为了给被装饰 对象,增加新功能,或者说增加某种能力 在程序中工具就是函数 如此一来,装饰器指的就是一个函数,被装饰着也是一个函数 总结;装饰器就是用一个函数去拓展另外一个已存 ...

  4. android组件之TabHost

    一 介绍 1.1 TAB的容器.这个对象包含两个子元素: TabWidget:管理标签(tabs),用户点击来选择一个特定的标签,是它告诉TabHost去切换界面的 FrameLayout:对象显示该 ...

  5. javaSocket笔记

    1.查看电脑当前开放链接的端口号 使用netstat命令 netstat -nao 可以查看PID进程号 netstat -nab 获取进程的详细信息 2.类创建的不同对象,引用是不同的.也就是说同一 ...

  6. Imperial roads 非严格次小生成树

    cf测评姬比uva快了五倍... /* 不管这条边是不是在mst上,直接跑lca求出路径上的最大边w即可 ans=mst-w+dist(u,v) */ #include<bits/stdc++. ...

  7. Wowza 相关

    下载: 1.https://www.wowza.com/pricing/installer 安装: https://www.wowza.com/docs/how-to-install-and-conf ...

  8. vue-cli watch简单用法

    创建一个vue单文件 <template> <div id="test"> <h4 @click="changeMsg()" id ...

  9. Hadoop ConnectException: Connection refused的一种解决办法

    跟着视频学习天气案例,把代码敲好,准备提交运行时才发现集群没启动.然后在node02.node03.node04使用zkServer.sh start启动ZooKeeper,然后在node01使用st ...

  10. python---数学表达式的分析树实现

    先走一遍, 前面很多知道点,都串起来了. # coding = utf-8 # 使用列表实现栈的功能 class Stack: def __init__(self): self.items = [] ...