在翻阅了一万篇博客 浏览了n多个网站以后 终于被我整出来一个 还算稳定可靠的 tcp通信工具类了 各位觉得有帮助的可以点个收藏
2021年8月11日 16:37:56 记
本代码为个人总结 经过验证 可以实现 多个端口 多个地址的长连接通信 并做了比较多的代码冗余 加强代码的稳定性可以直接上手使用
注意:本文章只提供java客户端部分代码,c++部分为服务端代码 本人使用的为测试工具 并没有搭建相对应的服务器代码 如果有需要测试软件的
可以前往我的个人空间下载
下载地址:c++socket测试软件(客户端+服务器)
话不多说 直接放代码
SocketClient 部分
/**
* @author JingJian
* @version 1.0
* @date 2021/8/10 15:55
*/
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.stereotype.Component;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@Slf4j
public class SocketClient {
private Socket client;
// socket连接列表 使用map保证唯一性
private Map<String, Socket> clientList = new HashMap<>();
AtomicInteger ai = new AtomicInteger(0);
// 输出流
private OutputStreamWriter writer;
// 输入流
private DataInputStream in;
/*public SocketClient() {
if (null == client) {
synchronized (SocketClient.class) {
if (null == client) {
try {
client = new Socket(host, port);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}*/
/**
* 创建新的连接
*
* @param host 请求地址
* @param port 端口
* @return
*/
public Socket getClient(String host, Integer port) {
// 请求地址
try {
if (null == client) {
synchronized (SocketClient.class) {
if (null == client) {
try {
client = new Socket(host, port);
// 把socket的信息存到map里面
clientList.put((client.getInetAddress().toString().replaceAll("/", "") + client.getPort()), client);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 如果需要新增连接 则判断端口或者地址是否相等
} else if (client.getPort() != port || !host.equals(client.getInetAddress().toString().replaceAll("/", ""))) {
// 遍历比较是否存在相对应的socket
for (String key : clientList.keySet()) {
// 存在相对应的socket
if (key.equals(host + port)) {
client = clientList.get(key);
return client;
}
}
// 不存在相对应的socket
try {
client = new Socket(host, port);
// 把socket的信息存到map里面
clientList.put((client.getInetAddress().toString().replaceAll("/", "") + client.getPort()), client);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
log.error(e.toString());
}
return client;
}
/**
* 发送消息
*
* @param msg 要发送的通知
* @return
*/
public String sendMsg(String host, Integer port, String msg) {
getClient(host, port);
//原子型锁,底层采用了锁机制
try {
in = new DataInputStream(client.getInputStream());
writer = new OutputStreamWriter(client.getOutputStream(), "gbk");//将写入的字符编码成字节后写入一个字节流
writer.write(msg);
writer.flush();
// new byte[5555] 5555 单次接收最长的字节数 目前没有找到更好的方法
byte[] readResult = new byte[5555];
in.read(readResult);
String result = new String(readResult, "gbk");
// 判断是否socket服务强行中断 如果强行中断 则返回的为 指定长度的 '' char字符 如5555个null 此时取长度或者判空无效
if (result.charAt(1) == '\0') {
throw new NumberFormatException("服务异常中断");
}
// 响应成功以后把错误次数计数清零
ai.getAndSet(0);
return result;
} catch (Exception e) {
log.error(e.toString());
try {
// 在指定的异常里面重新创建连接
if (e instanceof SocketTimeoutException || e instanceof NoRouteToHostException || e instanceof SocketException || e instanceof ConnectException || e instanceof NumberFormatException || e instanceof ClientAbortException) {
client = new Socket(host, port);
}
} catch (Exception ex) {
log.error(e.toString());
}
// 如果小于指定错误次数 则继续调用自身
if (ai.get() < 10) {
// 原子自增
ai.getAndIncrement();
return this.sendMsg(host, port, msg);
}
}
return "服务异常,请稍后重试!";
}
/**
* 关闭连接
*/
public void closeSocket() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
调用部分
//注入
@Autowired
private SocketClient socketClient;
//具体使用方法
socketClient.sendMsg("127.0.0.1", 7777, "7777测试数据");
//具体使用方法 第二个
socketClient.sendMsg("127.0.0.1", 8891, "8891测试数据");
//手动关闭连接方法 一般web不需要关闭 服务停了就会自动关闭了
socketClient.closeSocket();
测试结果: