java.net.SocketTimeoutException: Read timed out,tcp连接心跳[TCP Keep-Alive],socket模拟http

这篇具有很好参考价值的文章主要介绍了java.net.SocketTimeoutException: Read timed out,tcp连接心跳[TCP Keep-Alive],socket模拟http。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

            logger.info("接口开始");            
            con.setConnectTimeout(5000);//建立连接的超时时间,单位毫秒
            con.setReadTimeout(3 * 60 * 60 * 1000); //接口最长处理3小时

            // 开启连接
            con.connect();
            //请求参数
            if (params != null && params.length() > 0) {
                os = con.getOutputStream();
                os.write(params.getBytes(StandardCharsets.UTF_8));
                os.flush();
            }
            //响应
            logger.info("接口结果:" + con.getResponseCode()); // 这里抛出异常 java.net.SocketTimeoutException: Read timed out

 读超时con.setReadTimeout(3 * 60 * 60 * 1000);已设置为3小时。

日志

2023-06-02 05:16:44.341 logback [main] INFO  com.ustc.transfer.TransferService - 接口开始
2023-06-02 08:16:44.389 logback [main] ERROR com.ustc.transfer.TransferService - Read timed out
java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1607)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1512)
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
	at com.ustc.transfer.TransferService.sendPost(TransferService.java:489)

日志发现 等待了 3小时,抛出了异常

经过排查,是因为 后端防火墙,连接空闲20分钟,连接就会被丢弃。

解决办法是,使用 socket.setKeepAlive(true);
注意HttpURLConnection的connection.setRequestProperty("Connection", "keep-alive");是不能达到到效果的,这个作用是是复用连接。

注意与HTTP中请求头”Connection: keep-alive“的区别

socket.setKeepAlive(true)这个参数与TCP KeepAliveTime参数配合使用,默认2小时,空闲2小时的连接发送一次心跳防止连接被杀掉。
 

我这里的场景是,这个接口后台要处理3小时左右,而后端防火墙会丢弃空闲20分钟的连接

客户端是windows系统,TCP KeepAliveTime默认是连接空闲2小时才发送一次[TCP Keep-Alive]报文。根据我的实际情况改成15分钟。

在 cmd 中输入 regedit 打开注册表,复制下面的路径回车

计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

java.net.sockettimeoutexception,java,Read timed out

右键 Parameters > 新建 > DWORD(32位)> KeepAliveTime > 右键修改

 java.net.sockettimeoutexception,java,Read timed out

java.net.sockettimeoutexception,java,Read timed out

修改后,需要重启电脑,这样连接空闲15分钟后,就会发送一次[TCP Keep-Alive]报文。

socket.setSoTimeout(3 * 60 * 60 * 1000);//读超时,单位毫秒
socket.setKeepAlive(true);

package com.study;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * Socket模拟Http客户端
 * @date 2023/3/31 18:05
 */
public class SocketHttpClient {
    private static final Logger logger = LoggerFactory.getLogger(SocketHttpClient.class);

    public static void main(String[] args) throws Exception {
        String url = "http://localhost:5033/temp?t=21";
        // 请求参数:name=vaue
        Map<String, String> params = new HashMap<>();
        params.put("name", "张三");
        params.put("age", "18");
        String sendPost = sendPost(url, params);
        logger.info(sendPost);
    }

    /**
     * 模拟POST请求
     * @param url    请求路径
     * @param params 请求参数
     * @author 
     * @date 2023/3/31 17:42
     */
    public static String sendPost(String url, Map<String, String> params) {
        Socket socket = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            URI uri = new URI(url);
            // 要连接的服务端IP地址和端口
            String host = uri.getHost();
            int port = uri.getPort();
            // 与服务端建立连接
            socket = new Socket(host, port);
            // socket.setSoTimeout(5000);//读超时,单位毫秒
            socket.setSoTimeout(3 * 60 * 60 * 1000);//读超时,单位毫秒
            socket.setTcpNoDelay(true);//数据不作缓冲,立即发送
            // 这个参数与TCP KeepAliveTime参数配合使用,默认2小时,空闲2小时的连接发送一次心跳防止连接被杀掉。
            // 注意与HTTP中请求头”Connection: keep-alive“的区别
            // 修改注册表”计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters“新建”KeepAliveTime“值为900000即15分钟。
            socket.setKeepAlive(true);

            // 建立好连接后,从socket中获取输入流
            inputStream = socket.getInputStream();
            // 建立好连接后,从socket中获取输出流
            outputStream = socket.getOutputStream();

            // 获取http模拟报文
            byte[] http = getHttp(url, params);
            outputStream.write(http);
            outputStream.flush();

            // 测试发现,HTTP报文的终止符应该是"\r\n\r\n"即连续两个换行符,这部分内容刚好是请求头中的内容,
            // 假如请求头中没有"Content-Length",则请求体中的内容会被丢弃,
            // 如果有"Content-Length"请求头,后端继续读取"Content-Length"请求头指定大小的内容。

            // 在模拟HTTP请求时不要主动关闭连接,HTTP通过"Content-Length"请求头指定的大小来接收请求体中的数据。
            // 所以模拟HTTP报文时,是不需要shutdownOutput()关闭(单向)连接的,
            // 若shutdownOutput()关闭了连接,在120秒后,客户端会发送一个RST(连接重置)信号,并抛出异常java.net.SocketException: Connection reset
            
            // 请求报文发送完成后,发送终止符
            // socket.shutdownOutput();// 单向关闭输出流,发送流的终止符-1。

            // 获取响应内容
            byte[] buf = new byte[1024];
            int len;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //只有当客户端关闭它的输出流的时候,服务端才能取得结尾的-1
            while ((len = inputStream.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            // 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
            String result = out.toString(StandardCharsets.UTF_8.name());
            // 响应头和响应体之间有一个空行,这里只返回响应体中的内容
            if (result != null && result.indexOf("\r\n\r\n") != -1) {
                return result.substring(result.indexOf("\r\n\r\n") + 4);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 拼接http报文
     * @param url    请求路径
     * @param params 请求参数
     * @author 
     * @date 2023/3/31 17:47
     */
    private static byte[] getHttp(String url, Map<String, String> params) throws Exception {
        URI uri = new URI(url);
        // 要连接的服务端IP地址和端口
        int port = uri.getPort();
        String host = uri.getHost() + ":" + port;

        // 拼接http请求体
        StringBuilder bodyBuilder = new StringBuilder();
        if (params != null && params.size() > 0) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                // 参数名
                bodyBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()));
                bodyBuilder.append("=");
                // 参数值
                bodyBuilder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()));
                bodyBuilder.append("&");
            }
            bodyBuilder.deleteCharAt(bodyBuilder.length() - 1);
        }
        byte[] body = bodyBuilder.toString().getBytes(StandardCharsets.UTF_8);

        // 定义存放 http 报文的缓冲区
        ByteArrayOutputStream http = new ByteArrayOutputStream();
        http.write(("POST " + url + " HTTP/1.1\r\n").getBytes(StandardCharsets.UTF_8));
        http.write(("Host: " + host + "\r\n").getBytes(StandardCharsets.UTF_8));
        // 请求参数为key1=value1&key2=value2形式
        http.write(("Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n").getBytes(StandardCharsets.UTF_8));
        // 请求参数为json格式
        // http.write(("Content-Type: application/json; charset=UTF-8\r\n").getBytes(StandardCharsets.UTF_8));
        http.write(("Content-Length: " + body.length + "\r\n").getBytes(StandardCharsets.UTF_8));
        // http.write(("Cookie: " + cookie + "\r\n").getBytes(StandardCharsets.UTF_8));
        // 请求头和请求体之间有一个空行
        http.write("\r\n".getBytes(StandardCharsets.UTF_8));
        http.write(body);
        return http.toByteArray();
    }


}

// 请求报文发送完成后,发送终止符
// socket.shutdownOutput();// 单向关闭输出流,发送流的终止符-1。 

测试发现,HTTP报文的终止符应该是"\r\n\r\n"即连续两个换行符,这部分内容刚好是请求头中的内容,
假如请求头中没有"Content-Length",则请求体中的内容会被丢弃,
如果有"Content-Length"请求头,后端继续读取"Content-Length"请求头指定大小的内容。

在模拟HTTP请求时不要主动关闭连接shutdownOutput(),HTTP后端通过"Content-Length"请求头指定的大小来接收请求体中的数据。
所以模拟HTTP报文时,是不需要shutdownOutput()关闭(单向)连接的,
若shutdownOutput()关闭了连接,在120秒后,客户端会发送一个RST(连接重置)信号,并抛出异常java.net.SocketException: Connection reset

现象1:
请求报文发送完成后,如果关闭shutdownOutput单向关闭输出流,如果后台处理时间超过120秒,客户端会发送一个RST(连接重置)信号,并抛出异常Connection reset。

现象2:
请求报文发送完成后,如果不关闭shutdownOutput单向关闭输出流,会延迟20秒才能接收到后台的响应结果。

问:不关闭shutdownOutput响应结果为什么会延迟20秒?

这是因为在使用Socket发送HTTP报文时,服务器端会根据Content-Length或Transfer-Encoding字段来判断报文是否已经发送完毕。如果客户端没有主动关闭输出流,服务器端会一直等待接收数据,直到超时。因此,如果不调用shutdownOutput方法关闭输出流,可能会导致数据发送不完整,服务器端一直等待直到超时。而shutdownOutput方法会显式地告知服务器端数据已经发送完毕,服务器端不必等待超时,从而提高响应速度。

问:为什么关闭shutdownOutput单向关闭输出流,在请求时长超过120秒时会报错Connection resetd?

如果调用shutdownOutput方法关闭输出流,但请求时长超过了120秒,就会因为TCP连接超时而导致连接被重置,从而导致Connection reset错误。这是因为TCP连接有一个默认的超时时间,如果在超时时间内没有收到对方的回复,就会认为连接已经失效,从而重置连接。

问:请求报文发送完成后,到底该不该shutdownOutput()关闭连接?

1、如果请求在120秒内就会有响应结果,强烈建议关闭shutdownOutput连接。

2、如果请求不知道什么时候返回响应结果,有两种方案:

方案一:
关闭shutdownOutput连接,并修改
KeepAliveTime=60000毫秒,即1分钟就会发送一次[TCP Keep-Alive]报文,连接就不会超时被重置。

方案二:
不关闭shutdownOutput连接,这样做有个缺点,接收响应结果会晚20秒。
文章来源地址https://www.toymoban.com/news/detail-731146.html

到了这里,关于java.net.SocketTimeoutException: Read timed out,tcp连接心跳[TCP Keep-Alive],socket模拟http的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SocketTimeoutException:Read timed out问题解决org.apache.cxf.interceptor.Fault:Could not receive Message

    利用WebService给其它服务器地址发送请求时,遇到了一个报错: org.apache.cxf.interceptor.Fault:Could not receive Message。 控制台上显示: java.net.SocketTimeoutException:Read timed out。 搜了一下发现网上大部分是分享 org.apache.cxf.interceptor.Fault:Could not send Message。 发送问题的,很少有关于接收报错

    2024年02月05日
    浏览(41)
  • Finalshell连接VMWare虚拟机遇到java.net.ConnectException: Connection timed out: connect

     这个问题困扰了我很久,再网上搜了很多相关博客,最后找到了问题所在。 首先你要确定虚拟机是开启的状态,其连接的IP地址正确,虚拟机查看IP地址命令 ifconfig 我之前下载VMware之后删除过(我之前删除时,在网上找的完全删除vmware教程,就是这时候给VMware的网卡删除了

    2024年02月14日
    浏览(52)
  • finalshell连接VM虚拟机报错,java,net.ConnectException: Connection timed out: connect

    适用于,所有第三方连接虚拟机报错。 java,net.ConnectException: Connection timed out: connect Xshell啊什么的。 解决方法: 首先,我想确认一下是否已经安装了finalshell软件并且要连接的CentOS 7服务器已经设置好了。连接不上的问题有很多原因,这里给出一些建议来帮助您排查问题: 确保

    2024年02月05日
    浏览(56)
  • FinalShell连接Linux虚拟机报错java.net.ConnectException: Connection timed out: connect

    错误显示:   解决方法: 使用指令ip addr,在虚拟机检查网关是否配置。如果ens33的inet有ip地址,则说明网关有进行配置。  点击win+r,执行cmd,在出现的窗口中ping 虚拟机的地址  发现连接超时,说明虚拟机和计算机网络存在连接问题。查看虚拟机配置。 虚拟机的ip和子网

    2024年02月11日
    浏览(53)
  • 解决Read timed out和connect timed out的问题

    如果在新建Android项目时报错: Read timed out或者connect timed out 一定可以解决问题的办法如下: 第一步:打开项目下gradle中的第二个.properties文件,找到这个文件的下载路径,、一般卡顿是因为下载这个文件时网速较慢或者是因为在国外的原因。 第二步:按照文档中的路径,选

    2024年02月06日
    浏览(55)
  • finalshell提示java.net.ConnectException: Connection timed out: connect

    为什么超时? 事情是这样的,为了不向学校低头,我没有开通校园网,买了流量卡,于是每次都给电脑开热点,朋友叫我吃饭,我直接就走了,干完饭回来,在finalshell里面它提示java.net.ConnectException: Connection timed out: connect,我去检查了虚拟机是不是在开着,我的Linux是不是在

    2023年04月22日
    浏览(37)
  • Java连接redis报错timed out问题解决

    之前写了一篇文章:Spring redis使用报错Read timed out排查解决,解决超时问题 后面发现解决的不彻底,仍有报错 详细查看了下源码,发现不同版本里,参数名不一样,之前的参数设置,并没有在新版本里生效 核心报错提示是这一句: JedisConnectionException: java.net.SocketTimeoutExcept

    2024年01月19日
    浏览(44)
  • hive read time out

    hive-jdbc版本:2.3.9 报错信息如下: 问题定位:客户端的socket连接读超时,默认超时时间为30秒,需要在应用端增大读超时时间。 解决方案: 1)hiveConnection在使用socket连接时,设置了超时时间为30秒,超时时间较短,在运行稍微复杂点的SQL时,就会导致超时。如下图。    2) 

    2024年02月15日
    浏览(46)
  • flutter项目下载gradle出现Connect timed out或Read timed out

    当出现Connect timed out或Read timed out这有两种情况的时候首先检查自己的网络情况    如果网络没有问题就进入项目的gradle文件的gradle-wrapper.properties中可以看到默认的 这个地址是需要魔法上网才能进行访问 。 所以我们可以切换为腾讯的国内镜像地址进行下载 腾讯的国内镜像:

    2024年01月19日
    浏览(46)
  • jmeter返回请求Read timed out

    问题现象: 整个接口请求响应时间5020ms 如果已经对比过jmeter和postman请求的所有参数:内容编码、端口号、请求协议http/https、请求头等以上参数都一致的情况下,可以尝试设置接口的请求响应的超时时间,如下: 超时说明: 连接1000ms,意思为等待服务器连接1000ms,不管此时

    2024年02月15日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包