通过socket在服务间传输数据
完整代码
1. 基于Tcp协议的简单Socket通信
Socket通信步骤:(简单分为4步)
1.建立服务端ServerSocket和客户端Socket
2.打开连接到Socket的输出输入流
3.按照协议进行读写操作
4.关闭相对应的资源
代码实现
服务端Server.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
/**
Socket服务端
1.创建ServerSocket对象,绑定并监听端口
2.通过accept监听客户端的请求
3.建立连接后,通过输出输入流进行读写操作
4.关闭相关资源
*/
public static void main(String[] args) {
try {
ServerSocket serverSocket=new ServerSocket(8888);
System.out.println("服务端已启动,等待客户端连接..");
Socket socket=serverSocket.accept();//侦听并接受到此套接字的连接,返回一个Socket对象
//根据输入输出流和客户端连接
InputStream inputStream=socket.getInputStream();//得到一个输入流,接收客户端传递的信息
InputStreamReader inputStreamReader=new InputStreamReader(inputStream);//提高效率,将自己字节流转为字符流
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);//加入缓冲区
String temp=null;
String info="";
while((temp=bufferedReader.readLine())!=null){
info+=temp;
System.out.println("已连接客户端===");
System.out.println("服务端接收到客户端信息:"+info+",当前客户端ip为:"+socket.getInetAddress().getHostAddress());
}
OutputStream outputStream=socket.getOutputStream();//获取一个输出流,向服务端发送信息
PrintWriter printWriter=new PrintWriter(outputStream);//将输出流包装成打印流
printWriter.print("你好,===服务端===已接收信息");
printWriter.flush();
socket.shutdownOutput();//关闭输出流
//关闭相对应的资源
printWriter.close();
outputStream.close();
bufferedReader.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端Client.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
/**
Socket客户端
1.创建Socket对象,指定服务端的地址和端口号
2.建立连接后,通过输出输入流进行读写操作
3.通过输出输入流获取服务器返回信息
4.关闭相关资源
*/
public static void main(String[] args) {
try {
//创建Socket对象
Socket socket=new Socket("localhost",8888);
//根据输入输出流和服务端连接
OutputStream outputStream=socket.getOutputStream();//获取一个输出流,向服务端发送信息
PrintWriter printWriter=new PrintWriter(outputStream);//将输出流包装成打印流
printWriter.print("我是===客户端===");
printWriter.flush();
socket.shutdownOutput();//关闭输出流
InputStream inputStream=socket.getInputStream();//获取一个输入流,接收服务端的信息
InputStreamReader inputStreamReader=new InputStreamReader(inputStream);//包装成字符流,提高效率
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);//缓冲区
String info="";
String temp=null;//临时变量
while((temp=bufferedReader.readLine())!=null){
info+=temp;
System.out.println("客户端接收===到的信息:"+info);
}
//关闭相对应的资源
bufferedReader.close();
inputStream.close();
printWriter.close();
outputStream.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上代码实现了单客户端和服务端的连接,若要实现多客户端操作,需要涉及到多线程,只要你把每个接收到的Socket对象单独开一条线程操作,然后用一个死循环while(true)去监听端口就行,这边直接给代码了
2. 多线程实现Socket通信
线程操作类:SocketThread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/**
* Socket多线程处理类 用来处理服务端接收到的客户端请求(处理Socket对象)
*/
public class SocketThread extends Thread {
private Socket socket;
public SocketThread(Socket socket) {
this.socket = socket;
}
public void run() {
// 根据输入输出流和客户端连接
try {
InputStream inputStream = socket.getInputStream();
// 得到一个输入流,接收客户端传递的信息
InputStreamReader inputStreamReader = new InputStreamReader(
inputStream);// 提高效率,将自己字节流转为字符流
BufferedReader bufferedReader = new BufferedReader(
inputStreamReader);// 加入缓冲区
String temp = null;
String info = "";
while ((temp = bufferedReader.readLine()) != null) {
info += temp;
System.out.println("已接收到客户端连接");
System.out.println("服务端接收到客户端信息:" + info + ",当前客户端ip为:"
+ socket.getInetAddress().getHostAddress());
}
OutputStream outputStream = socket.getOutputStream();// 获取一个输出流,向服务端发送信息
PrintWriter printWriter = new PrintWriter(outputStream);// 将输出流包装成打印流
printWriter.print("你好,服务端已接收到您的信息");
printWriter.flush();
socket.shutdownOutput();// 关闭输出流
// 关闭相对应的资源
bufferedReader.close();
inputStream.close();
printWriter.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端类:Server
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
/**
* Socket服务端
*/
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端已启动,等待客户端连接..");
while (true) {
Socket socket = serverSocket.accept();// 侦听并接受到此套接字的连接,返回一个Socket对象
SocketThread socketThread = new SocketThread(socket);
socketThread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端类:Client
同单线程
3. 基于Socket传递对象
在实际开发中,基于Socket编程,一般传递的并非字符串,很多情况下是对象,我们可以使用ObjectOutputStream将输出流对象序列化。
OutputStream outputStream = socket.getOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
User user=new User("admin","123456");
objectOutputStream.writeObject(user);
4.将对象转换成XML报文传输
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.11</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
实体类:Test
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//data.model下放传输接收参数封装
@Data
@AllArgsConstructor
@NoArgsConstructor
//每封装一个加此注解给此类起别名(别名最好与实体类名一致),切记一定加上
//如果XStream解析失败 别名需要改成类的路径
@XStreamAlias("com.i2f.paycore.model.data.Test2")
public class Test2 {
private String amount;
private String cardNumber;
}
通信:Socket
ServerInit
public class ServerInit {
//构造函数中调用,目的就是在类的初始化的时候进行调用。
public ServerInit(ServerStarter starter) {
starter.startServer();
}
}
Socket服务端:ServerStarter
public class ServerStarter {
private int port = 10110;
@Async("taskExecutor")
public void startServer() {
ServerSocket serverSocket = null;
Socket socket = null;
try {
System.out.println("boot核心系统-----开始初始化Socket服务端");
serverSocket = new ServerSocket(port);
while (true) {
System.out.println("boot核心系统-----Socket服务端准备接受请求并处理");
socket = serverSocket.accept();
new DealThead(socket).start();
// dealThead.handle(accept);
}
} catch (IOException e) {
e.printStackTrace();
}
}
BankBusClient
public class BankBusClient {
//TODO 改成配置方式
//核心系统服务器地址
private static final String MIN_SHENG_BANK_SOCKET_SERVER_IP = "127.0.0.1";
//核心系统服务器地址的端口号
private static final int MIN_SHENG_BANK__SERVER_PORT = 10110;
//有时new XStream会导致传送对象转换失败,需要在构造函数中添加参数new DomDriver
private static final XStream XSTREAM = new XStream(new DomDriver());
/**
* socket发送报文
*
* @param socket socket对象
* @throws IOException
*/
public static void sendAcctPackage(Socket socket, XmlPackage xmlPackage) throws IOException {
String packageStr;
// xStream处理成xml字符串
//类加载器
XSTREAM.setClassLoader(new BankBusClient().getClass().getClassLoader());
//开启类名添加注解别名解析 每一个解析的类都需要加上别名注解 @XStreamAlias("XmlPackage")
XSTREAM.autodetectAnnotations(true);
XSTREAM.processAnnotations(new Class[]{XmlPackage.class, Test2.class});
//将数据转换成XML格式
packageStr = XSTREAM.toXML(xmlPackage);
//报文数据写入
IOUtils.write(packageStr, socket.getOutputStream(), String.valueOf(StandardCharsets.UTF_8));
//关闭输出流
socket.shutdownOutput();
LOGGER.info("socket[{}:{}]写入信息:[{}]", socket.getInetAddress(), socket.getPort(), packageStr);
}
/**
* socket接收返回信息
*
* @param socket socket对象
* @return XmlPackage对象
* @throws IOException
*/
//TODO 应该返回Object或者泛型
public static XmlPackage receiveResponse(Socket socket) throws IOException {
//读取返回结果
byte[] bytes = IOUtils.toByteArray(socket.getInputStream());
String xmlStr = new String(bytes, StandardCharsets.UTF_8);
LOGGER.info("读取到响应报文[{}]", xmlStr);
//解析返回结果
XSTREAM.setClassLoader(new BankBusClient().getClass().getClassLoader());
XSTREAM.alias("XmlPackage", XmlPackage.class);
XSTREAM.autodetectAnnotations(true);
XSTREAM.processAnnotations(new Class[]{XmlPackage.class, Test2.class});
return (XmlPackage) XSTREAM.fromXML(xmlStr);
}
}
DealThead
public class DealThead extends Thread{
// @Description: 这里为接收民生传过来的标识符调用相关方法
private Socket socket;
@Autowired
Test2Service test2Service;
public DealThead() {
}
public DealThead(Socket socket) {
this.socket=socket;
this.test2Service = new Test2Service() {
@Override
public void test2(XmlPackage xmlPackage) {
}
};
}
@SneakyThrows
@Override
public void run() {
//接收客户端的socket 并解析XML
XmlPackage xmlPackage = BankBusClient.receiveResponse(socket);
log.info("springboot核心系统-------接收数据完毕");
String type = xmlPackage.getHeadType();
Object data=xmlPackage.getBodyData();
log.info(data.toString());
switch (type) {
//通过case的标识数字调用相关方法,调用完之后break返回
case "9527":
test2Service.test2(xmlPackage);
break;
default:
break;
}
BankBusClient.sendAcctPackage(socket, xmlPackage);
log.info("springboot系统-------返回数据完毕");
}
XML报文统一格式类:XmlPackage
/**
* XML报文统一格式类
*/
@XStreamAlias("XmlPackage")
public class XmlPackage<T> {
private Head head = new Head();
private Body body = new Body();
public String getHeadCode() {
return head == null ? null : head.code;
}
public void setHeadCode(String code) {
head.code = code;
}
public String getHeadMessage() {
return head == null ? null : head.message;
}
public void setHeadMessage(String message) {
head.message = message;
}
public String getHeadSerialNum() {
return head == null ? null : head.serialNum;
}
public void setHeadSerialNum(String serialNum) {
head.serialNum = serialNum;
}
public String getHeadType() {
return head == null ? null : head.type;
}
public void setHeadType(String type) {
head.type = type;
}
public T getBodyData() {
return body == null ? null : body.data;
}
public void setBodyData(T data) {
body.data = data;
}
public List<T> getBodyDataList() {
return body == null ? null : body.dataList;
}
public void setBodyDataList(List<T> data) {
body.dataList = data;
}
/**
* 报文头部信息
*/
private class Head{
/**
* 序列号 随机生成
*/
private String serialNum;
/**
* 处理类型 交易类型
*/
private String type;
/**
* 状态码
*/
private String code;
/**
* 消息
*/
private String message;
}
/**
* 报文数据 统一返回格式 responseResult
*/
private class Body{
/**
* 数据
*/
private T data;
/**
* 集合数据
*/
private List<T> dataList;
}
}
待整理
Sys_conf配置通信端口和地址
public interface Sys_conf {
interface Sys{
int port=2369;
String address="localhost";
}
Client
public class Client {
private Socket socketClient;
/**
* 启动客户端socket
* @throws IOException
*/
public Client() throws IOException {
Socket socket = new Socket(Sys_conf.Core_Sys.address, Sys_conf.Core_Sys.port);
socketClient=socket;
}
public void close() throws IOException {
socketClient.close();
}
//转成输出流
private void Str2Io(String msg) throws IOException {
byte[] bytes = msg.getBytes("utf-8");
OutputStream outputStream = socketClient.getOutputStream();
outputStream.write(bytes);
}
//读取输入流
private String Io2Str() throws IOException {
InputStream inputStream = socketClient.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuffer str = new StringBuffer();
while((len = inputStream.read(bytes))!=-1){
str.append(new String(bytes,0,len,"utf-8"));
bytes = new byte[1024];
}
return str.toString();
}
//发报文
public void send(String xml) throws IOException {
Str2Io(xml);
socketClient.shutdownOutput();
}
//收报文
public String onMassage() throws IOException {
String xml = Io2Str();
socketClient.shutdownInput();
return xml;
}
}
Serve
@Component
@Slf4j
public class Server {
public Server(MainHandle mainHandle) {
ServerSocket serverSocket = null;
try {
log.info("1.开始初始化Socket。。。");
serverSocket = new ServerSocket(Sys_conf.Sys.port);
log.info("成功初始化Socket");
while (true){
log.info("2.等待请求...只能启动一次,所以需要一直监听");
Socket accept = serverSocket.accept();
log.info("拿到请求开始处理...");
mainHandle.handle(accept);//通过MainHandle判断具体的handle
}//定时任务
} catch (Exception e) {
log.info("系统异常:"+e.getMessage());
}
}
}
MainHandle
@Component
@Slf4j
@AllArgsConstructor
public class MainHandle {
@Autowired
LoanHandle loanHandle; // 贷款
@Async("taskExecutor")
public void handle(Socket socket) throws IOException {
try {
log.info("开始处理请求。。。");
String xml = IoUtils.Io2Str(socket.getInputStream());
socket.shutdownInput();
//拿到报文头
Header header = (Header) XmlUtils.getHeader(xml, Header.class);
log.info("进行业务逻辑处理。。。");
//根据报文头判断使用哪一个业务
if (ReqTypeConfig.LOAN_APPLY.equals(header.getType())) {
loanHandle.ApplyLoan(xml, socket);// 贷款申请
}
} catch (Exception e) {
//处理异常
log.info("业务逻辑异常:" + e.getMessage());
Message reMessage = new Message();
Header reHeader = new Header();
Msg msg = new Msg();
if (e instanceof CoreRunTimeException) {
CoreRunTimeException ex = (CoreRunTimeException) e;
reHeader.setType(ex.getCode());
msg.setMsg(ex.getMessage());
} else {
reHeader.setType(ReMsgCodeConfig.CODE.FAILED);
msg.setMsg(ReMsgCodeConfig.MSG.FAILED);
}
reMessage.setHeader(reHeader);
reMessage.setBody(msg);
String reMessageXml = XmlUtils.bean2Xml(reMessage, Message.class, Header.class, Msg.class);
OutputStream outputStream = socket.getOutputStream();
IoUtils.Str2Io(outputStream, reMessageXml);
socket.shutdownOutput();
}
}
}
client发送到server
@Transactional
public int loanIdentity(Context context) throws Exception {
User user = new User();
String name = (String) context.getValueAt("name");
user.setName(name);
//创建报文以及报文头
Message message = new Message();
Header header = new Header();
header.setId(UUID.randomUUID().toString());
//type是后台与核心系统交互的唯一标识
header.setType(ReqType.LOAN_INFO);
//将报文头放入message
message.setHeader(header);
message.setBody(user);
//封装报文
String s1 = XmlUtils.bean2Xml(message, Message.class, Header.class, User.class);
//创建客户端连接
Client client = new Client();
client.send(s1);
//接收报文
String s = client.onMassage();
//转换报文格式
// String msgXml = XmlUtils.bean2Xml(s,);
//将报文转换成message
Header reheader = (Header) XmlUtils.getHeader(s, Header.class);
String type = reheader.getType();
if ("000000".equals(type)){
Message message1 = (Message) XmlUtils.xml2bean(s,Message.class,Header.class, User.class);
//通过xml2Bean转换成相应的bean类
User body = (User) message1.getBody();
context.setValueAt("data", DataDeformer.deformBean(body));
return 0;
}
Object o = XmlUtils.xml2bean(s,Message.class, Header.class, Msg.class);
Message message1 = (Message) o;
Msg body = (Msg) message1.getBody();
String msg = body.getMsg();
PkgUtil.setHead(context,type,msg);
return 1;
}
server将数据返回到client
public void LoanInfo(String xml,Socket socket) throws Exception{
//创建返回报文以及报文头
Message reMessage = new Message();
Header reHeader = new Header();
Message message = (Message) XmlUtils.xml2bean(xml, Message.class, Header.class, User.class);
User user = (User) message.getBody();
if (user.getUseridcardnumber() == null || user.getName() == null) {
log.info("身份证或用户名没拿到");
throw new CoreRunTimeException(ReMsgCode.CODE.NO_USERIDANDNAME, ReMsgCode.MSG.NO_USERIDANDNAME);
}
String userid = user.getUserid();
String useridcardnumber = user.getUseridcardnumber();
String name = user.getName();
//查出数据库中的用户
User user1 = userMapper.selectByPrimaryKey(userid);
if (user1.getUseridcardnumber() != useridcardnumber || user1.getName() != name) {
log.info("用户验证失败,姓名与该账户不匹配");
throw new CoreRunTimeException(ReMsgCode.CODE.ERROR_NAME, ReMsgCode.MSG.ERROR_NAME);
}
Msg msg = new Msg();
//设置返回消息
msg.setMsg(ReMsgCode.MSG.SUCCESS_NAME);
reMessage.setBody(msg);
reMessage.setHeader(reHeader);
reHeader.setType(ReMsgCode.CODE.SUCCESS);
reMessage.setHeader(reHeader);
//封装报文
String reMessageXml = XmlUtils.bean2Xml(reMessage, Message.class, Header.class, Msg.class);
//写入输出流
OutputStream outputStream = socket.getOutputStream();
IoUtils.Str2Io(outputStream, reMessageXml);
socket.shutdownOutput();
}