在网络编程中,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. 如何在PDF中添加水印,PDF添加水印技巧

    PDF文件现在的使用很是普遍,不管是工作中还是学习中都会使用到PDF文件,制作一个PDF文件就很辛苦的,我们要是想把PDF文件中添加水印防止抄袭的时候应该要怎么做呢,其实吧PDF文件添加水印还挺简单的 ...

  2. python网络爬虫笔记(八)

    一.pthon 序列化json格式 1.将python内置对象转换成json 模块,dumps()方法返回的是一个str,内容是标准的JSON,dump()方法可以直接吧JSON写入一个file-li ...

  3. 饮冰三年-人工智能-linux-02 初始Linux

    参考博客:https://www.cnblogs.com/linhaifeng/articles/6045600.html 1:初始Linux命令 右击,开启终端,或者ctrl+alt[F1-F6]的 ...

  4. B: Ocean的游戏(前缀和)

    B: Ocean的游戏 Time Limit: 1 s      Memory Limit: 128 MB Submit My Status Problem Description 给定一个字符串s, ...

  5. OpenCV-Python入门教程2-打开摄像头

    一.打开摄像头 import cv2 # 打开摄像头并灰度化显示 capture = cv2.VideoCapture(0) while(True): # 获取一帧 ret, frame = capt ...

  6. azkaban使用

    新建一个text文件,a.job,打包成zip包传到azkaban即可 方式1:job流   1. a.job内容范例: type=command command=hive shell command ...

  7. python内置的魔术命令(builtin magic commands)

    在ipython或者jupyter notebook中,会出现"%"开头并且一个很短的命令,例如交互式的matlablib绘图: %matplotlib inline 之前一直不知 ...

  8. ubuntu下使用matplotlib绘图无法显示中文label

    原因是字体导致的.大家的做法基本都是搞一个windows上的字体文件(simhei.ttf, 点我fq下载)然后刷新一下缓存文件. 只不过百度搜到第一篇CSDN的博客,写的很不靠谱(不是所有的CSDN ...

  9. jexus linux x64 [专业版] 安装和配置https

    一.环境 操作系统:centOs7-x64 二.准备工作 购买SSL/TLS证书 三.部署 1.首先查看“/lib”或“/usr/lib”等系统库文件夹中是否有SSL库文件的名字,该文件名应该是“li ...

  10. [转] 合理使用npm version与npm dist-tag详解

    第一步:发布第一个稳定版本 npm publish//1.0.0 第二步:修改文件继续发布第二个版本 git add -A && git commit -m "c" ...