Java的Socket通信的断网重连的正确写法

news2024/11/24 19:13:01

Java的Socket通信的断网重连的正确写法

    • Socket通信的断网重连介绍
    • 客户端与服务端源码
    • 演示截图
      • 本地演示
      • 服务器演示
        • 演示截图
    • 总结

Socket通信的断网重连介绍

针对于已经建立通信的客户端与服务器,当客户端与服务器因为网络问题导致网络不通而断开连接了或者由于服务器端的服务被突然停掉,而客户端进行的一种尝试重新建立连接的操作;我采用的是Socket自带的往通道内写数据是否成功来判断当前连接是否断开,采用该方式的好处是简洁易懂,高效通用。

一般的采用ping命令,定时尝试重新建立新的连接的方式,都有一些不足之处,前者是没办法针对端口进行判断,效果不好;后者则是每次都建立新的连接,导致服务器端压力会较大,而且相对的代码逻辑也会更加复杂。而采用本文介绍的写数据的方式,即可简单判断连接是否断开;原理是当连接断开后,写数据时会报错,捕获该错误即可;底层原理则是Java的Socket底层代码,采用C语言或者C++编写,在那里肯定是去判断IP和端口是否可以访问到的逻辑;有需要的话,下篇文章可以查看源码,进一步熟悉Java的网络编程知识。

客户端与服务端源码

为了方便演示和理解,仅进行了服务端向客户端发送数据,客户端接收数据的单向通信方式;实际扩展也非常简单。可以由读者进行尝试实现,增加网络编程知识的熟练度。

服务端

package com;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author BBYH
 */
public class MyServerSocket {
    public static final Integer PORT = 8080;

    private static Socket myServerSocket;
    private static OutputStream outputStream;

    public static void listen() {
        System.out.println("服务端监听端口" + PORT);

        new Thread(() -> {
            try (ServerSocket serverSocket = new ServerSocket(PORT)) {
                while (true) {
                    try {
                        myServerSocket = serverSocket.accept();
                        System.out.println("服务端接收到来自客户端的连接");
                        outputStream = myServerSocket.getOutputStream();

                        new Thread(() -> {
                            Scanner scanner = new Scanner(System.in);
                            while (true) {
                                try {
                                    System.out.println("向客户端发送消息:");
                                    String sendContent = scanner.next();
                                    outputStream.write(sendContent.getBytes(StandardCharsets.UTF_8));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        listen();
    }
}

客户端

package com;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * @author BBYH
 * @description 通过socket的写数据,来判断当前的socket是否连通
 */
public class ClientSocket {
    public static final String IP = "127.0.0.1";
    public static final Integer PORT = 8080;

    private static Socket socket;
    private static InputStreamReader reader;
    private static OutputStream outputStream;

    public static void main(String[] args) {
        // 首次建立连接
        connect();
    }

    public static void connect() {
        System.out.println("向服务端端口" + PORT + "申请建立连接");
        try {
            socket = new Socket(IP, PORT);
            System.out.println("客户端与服务器端连接建立成功");
            reader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
            outputStream = socket.getOutputStream();

            // 开启读取线程
            openReadThread();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void openReadThread() {
        new Thread(() -> {
            // 开启断网重连线程
            new Thread(() -> {
                while (true) {
                    if (isDisContentWithServer()) {
                        while (true) {
                            try {
                                socket = new Socket(IP, PORT);
                                reader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
                                outputStream = socket.getOutputStream();
                                System.out.println(new Date() + ":重新建立连接成功");
                                break;
                            } catch (IOException e) {
                                System.out.println(new Date() + ":尝试重新建立连接失败");
                                try {
                                    Thread.sleep(3000);
                                } catch (InterruptedException ex) {
                                    throw new RuntimeException(ex);
                                }
                            }
                        }
                    }
                }
            }).start();

            char[] readBuf = new char[1024];
            while (true) {
                try {
                    if (reader.ready()) {
                        int read = reader.read(readBuf);
                        System.out.println("接收到来自服务端的消息:" + new String(readBuf, 0, read));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static boolean isDisContentWithServer() {
        try {
            outputStream.write(("发送数据").getBytes(StandardCharsets.UTF_8));
            Thread.sleep(3000);
            return false;
        } catch (Exception e) {
            return true;
        }
    }
}

尽管在客户端开启断网重连线程中进行了两次while循环,不是很优雅,但仍然可以较为清晰的理解到代码的执行逻辑;即,每隔固定时间(3秒)进行是否连通的判断,当发现不连通的,则进行尝试重连(重新建立连接),该处的逻辑没有办法省略,由于客户端一般不长时间运行,消耗的资源虽然较多,但在程序运行结束后也自然就释放了。

演示截图

演示流程为:开启服务器 – 客户端建立连接 – 服务器发送一些数据 – 服务器断开连接 – 客户端尝试重连(自动进行) – 重新开启服务器 – 服务器再次发送数据

在该过程中,服务器在首次开启后的关闭,即代表了客户端的断网,实际情况中可以采用服务器,然后采用断开Wifi的方式代替这个关闭服务器,只要保证了客户端与服务器无法连通,即代表断网了,后面的重连则可以采用再次打开Wifi,重新连接上网络,连接则自动建立好

本地演示

开启服务器 – 客户端建立连接 – 服务器发送一些数据
在这里插入图片描述

服务器断开连接 – 客户端尝试重连(自动进行)
在这里插入图片描述

重新开启服务器 – 服务器再次发送数据
在这里插入图片描述

服务器演示

采用的是我申请的阿里云服务器,流程与上述文字说明一致,采用断开Wifi的形式来代替服务器关闭的效果;服务器关闭同样被我测试过,与本地一样,可以进行重连。由于我采用Xshell进行连接,当断开连接后日志会失效,所以采用后台任务记录运行打印的日志;命令为 nohup java com.MyServerSocket > MyServerSocket.out &

关于不采用防火墙来演示断网效果,则是因为防火墙无法拦截已经建立好的Socket通道,经测试发现该现象,读者可手动进行验证,如有意外情况,可向我进行反馈。

由于nohup命令的输入重定向,我暂时还没研究明白,所以目前服务端也不进行发送数据,两边都只是简单的建立连接和断开,代码进行了简单的调整,如下:

服务器端

package com;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author BBYH
 */
public class MyServerSocket {
    public static final Integer PORT = 8080;
    private static Socket myServerSocket;

    public static void listen() {
        System.out.println("服务端监听端口" + PORT);

        new Thread(() -> {
            try (ServerSocket serverSocket = new ServerSocket(PORT)) {
                while (true) {
                    try {
                        myServerSocket = serverSocket.accept();
                        System.out.println("服务端接收到来自客户端的连接");
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        listen();
    }
}

客户端

package com;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * @author BBYH
 * @description 通过socket的写数据,来判断当前的socket是否连通
 */
public class ClientSocket {
    public static final String IP = "127.0.0.1";
    public static final Integer PORT = 8080;

    private static Socket socket;
    private static OutputStream outputStream;

    public static void main(String[] args) {
        // 首次建立连接
        connect();
    }

    public static void connect() {
        System.out.println("向服务端端口" + PORT + "申请建立连接");
        try {
            socket = new Socket(IP, PORT);
            System.out.println("客户端与服务器端连接建立成功");
            outputStream = socket.getOutputStream();

            // 开启断网重连线程
            openReConnectThread();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void openReConnectThread() {
        new Thread(() -> {
            while (true) {
                try {
                    if (isDisContentWithServer()) {
                        while (true) {
                            try {
                                socket = new Socket(IP, PORT);
                                outputStream = socket.getOutputStream();
                                System.out.println(new Date() + ":重新建立连接成功");
                                break;
                            } catch (IOException e) {
                                System.out.println(new Date() + ":尝试重新建立连接失败");
                                try {
                                    Thread.sleep(2000);
                                } catch (InterruptedException ex) {
                                    throw new RuntimeException(ex);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static boolean isDisContentWithServer() {
        try {
            outputStream.write(("发送数据").getBytes(StandardCharsets.UTF_8));
            Thread.sleep(2000);
            return false;
        } catch (Exception e) {
            return true;
        }
    }
}

演示截图

开启服务器 – 客户端建立连接
在这里插入图片描述

断开Wifi – 客户端尝试重新建立连接 – 重新打开Wifi – 连接成功
在这里插入图片描述

总结

本次断网重连演示完美结束,如有疑问,可在评论区进行提问

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1021725.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ET数据无效值处理(仅代表个人观点)

1将无效值去除查看,查看有效值 有效值范围为1830~9472 2.计算有效范围最大值最小值 如果计算四分数据得将栅格属性表导出execl进行计算 2根据该片文章进行替换 如何处理MODIS蒸散数据(ET)中的填充值(仅作参考)_Z_W_H_的博客-CSDN博客

大数据学习1.1-Centos8虚拟机安装

1.创建新的虚拟机 2.选择稍后安装OS 3.选择Linux的CentOS8 4.选择安装路径 5.分配20g存储空间 6.自定义硬件 7.分配2g内存 8.分配2核处理器 9.选择镜像位置 10.开启虚拟机安装 推荐密码设置为root

TS开发环境搭建

一、安装Node.js 官网:Node.js (nodejs.org) 可以去看别的大佬安装Node.js的文章,因为大部分人都安装了Node环境,这里我就不做说明 二、使用npm全局安装typescript 在终端中输入如下命令: npm i -g typescript 安装完成会有如…

程序开发:在线报名线下活动小程序源码功能解析

针对线下活动在线报名场景的小程序,支持在线支付费用以及线下核销。 可以应多大多数的线下报名,线上报名客服表单可以定制订单,支持导出报名,支持审核,支持分享,支持分销拓客以及线下核销。 WEB小程序双端…

2023年日经225指数研究报告

第一章 指数概况 1.1 概述 日经225指数,又被称为日经平均股票价格或日经225平均指数,通常被称为日经或日经指数,是东京证券交易所的一个重要股价指数。该指数自1950年起由日本经济新闻(The Nikkei)日报计算&#xff…

Chrome浏览器崩溃“STATUS_INVALID_IMAGE_HASH”的解决方法

原文地址:Chrome浏览器崩溃"STATUS_INVALID_IMAGE_HASH"的解决方法 | JUNES BLOG | 六月博客 今天无意间,发现Chrome浏览器多了个“ 由贵单位管理 ”的提示,出于安全的考虑,想立马把它搞掉,百度了一堆方法&a…

【编程实践】在VS studio中配置Eigen库

1 介绍 Eigen库是C标准模板库,能够进行向量运算、矩阵运算、矢量运算、数值分析等操作,并且包含相应的运算算法。 Eigen官方地址: 地址 可在官网下载指定版本的压缩包,将压缩包解压至后面配置的“附件包含目录”中。 2 配置 2.1 VS studi…

基于SSM的校园车辆管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

如何借助上线初期运维管理守住项目建设最后一公里

随着运营商技术升级、业务发展,以及服务能力要求提升,当下新建项目的交付或系统大版本升级大多数都需要历经千辛万苦才达到上线的彼岸。然而,项目上线并不意味着项目结束,“上线”也并不意味着终点,而是一个新的管理模…

Linux服务器初始化、yum安装java、redis、mysql

目录 前言 一、yum安装java二、yum安装redis三、yum安装mysql 前言 本文使用yum命令安装部署可能会用到的相关应用 安装软件包之前,我们需要先更新系统,以确保安装的软件包是最新的版本。执行以下命令: sudo yum update一、yum安装java 1、…

【阿里国际笔试】编程13

1.小红拿到了一个01串,她有以下两种操作: 1.选择一个字符取反,代价为x。 2.选择两个相邻的字符同时取反,代价为y。 小红想知道,自己将字符串变成全0”的最小代价是多少? 字符取反,指的是1变成0’"0变成1 样例 3 …

更新GitLab上的项目

更新GitLab上的项目 如有需要,请参考这篇:上传项目到gitlab上 1.打开终端,进入到本地项目的根目录。 2.如果你还没有将远程GitLab仓库添加到本地项目,你可以使用以下命令: 比如: git remote add origin …

响应式网页设计(Responsive Web Design)的核心原理

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 响应式网页设计的核心原理⭐ 优点和缺点优点缺点 ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚…

JUC第三讲:Java 并发-线程基础

JUC第三讲:Java 并发-线程基础 本文是JUC第三讲,主要概要性的介绍线程的基础,为后面的章节深入介绍Java并发的知识提供基础。 文章目录 JUC第三讲:Java 并发-线程基础1、带着BAT大厂的面试问题去理解2、线程状态转换2.1、新建(New…

Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

Unity 开发人员的城堡游戏引擎概述 一、简介2. Unity相当于什么GameObject?3. 如何设计一个由多种资产、生物等组成的关卡?4. 在哪里放置特定角色的代码(例如生物、物品)?Unity 中“向 GameObject 添加 MonoBehaviour”…

权限提升数据库(基于MySQL的UDF,MOF,启动项提权)

获取数据库权限 如何获取数据库的最高权限用户的密码,常用方法有这些 网站存在高权限SQL注入点 数据库的存储文件或备份文件 网站应用源码中的数据库配置文件 采用工具或脚本爆破 网站存在高权限SQL注入点 可以通过sqlmap拿到user表的账号密码,密码可能…

短视频矩阵系统源码开发分享

①账号的建立与发布频率 要根据品牌的定位和特点,结合平台的特点和用户需求,制作符合品牌及个人形象的账号名称和内容发布主旨,以在短视频平台建立起自身标签,从而提升品牌知名度和美誉度。 发文频率也很关键,发文频…

新增MariaDB数据库管理、支持多版本MySQL数据库共存,1Panel开源面板v1.6.0发布

2023年9月18日,现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.6.0版本。 在这个版本中,1Panel新增MariaDB数据库管理;支持多版本MySQL数据库共存;支持定时备份系统快照和应用商店中已安装应用;支持为防火墙…

零代码编程:用ChatGPT批量下载网站中的特定网页内容

http://blog.umd.edu/davidkass这个网站上有伯克希尔股东大会的一些文字稿,其标题如下: Notes From the Berkshire Hathaway 2020 Annual Meeting – May 2, 2020 Notes From the Berkshire Hathaway 2021 Annual Meeting – May 1, 2021 在右边的搜索…

MySQL 篇

目录 1、数据库三范式 2、数据库事务的特性 3、MySQL数据库引擎 4、说说 InnoDB 与 MyISAM 的区别 5、索引是什么? 6、索引数据结构 7、MySQL 索引类型有哪些? 8、索引有什么优缺点? 9、使用索引应该注意些什么? …