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();
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值