一、socket是什么?

Socket是一个类,用来在两个程序之间传输数据。一般用在服务端与客户端以流的方式传输数据。

二、使用步骤

服务端

先初始化一个ServerSocket服务端连接对象

@Configuration
@Getter
public class ServerSocketConfig {

    private ServerSocket serverSocket;

    private int port = 56789;

    /*
     * 初始化一个ServerSocket对象
     *
     * */
    @Bean
    public void createPool() throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

在项目启动时就启动服务端socket连接,等待客户端的连接,连接成功后会一直接收客户端传入的流数据,如果客户端断开连接会重新调用一个新的连接继续等待客户端重新创建连接

@Component
public class SocketServer implements CommandLineRunner {


    @Autowired
    private ServerSocketConfig serverSocketConfig;


    @Override
    public void run(String... args){
        //实现CommandLineRunner接口重写
        extracted();
    }

    private void extracted() {
        ServerSocket serverSocket = serverSocketConfig.getServerSocket();
            System.out.println("服务端socket启动成功");

        try {
            Socket socket = serverSocket.accept();
            System.out.println("socket链接成功");
            InputStream inputStream = socket.getInputStream();
            //保持长连接
            while (true) {
                //这里死循环不是一直在运行  如果流没数据会一直等待 所以这里可以直接用死循环
                byte[] bytes = new byte[1024];
                int length = inputStream.read(bytes);
                System.out.println("读取成功:"+new String(bytes,0,length));
            }
        } catch (Exception e) {
            //异常的话,重新建立一个链接等待连接
            System.out.println("链接关闭");
            extracted();
        }

    }
}

客户端

这里的客户端是使用Socket对象连接池进行连接

先创建一个连接池工厂

//工厂类
@Component
public class SocketFactory implements PooledObjectFactory<Socket> {

    private String host = "127.0.0.1";

    private int port = 56789;

    /*
     * 创建Socket对象
     * */
    @Override
    public PooledObject<Socket> makeObject() throws Exception {
        Socket socket = new Socket(host, port);
        return new DefaultPooledObject<>(socket);
    }

    /*
     * 销毁对象
     * */
    @Override
    public void destroyObject(PooledObject<Socket> p) throws Exception {
        p.getObject().close();
    }

    /*
     *检验对象是否有效
     * */
    @Override
    public boolean validateObject(PooledObject<Socket> p) {
        return p.getObject().isConnected();
    }

    /*
     * 初始化对象
     * */
    @Override
    public void passivateObject(PooledObject<Socket> p) throws Exception{
    }

    @Override
    public void activateObject(PooledObject<Socket> p) throws Exception {
    }

}

创建一个连接池对象

@Component
@Data
public class ConnectionPool {

    private final GenericObjectPool<Socket> pool;

    public ConnectionPool(PooledObjectFactory<Socket> factory) {
        pool = new GenericObjectPool<>(factory);
    }
}

初始化一个连接池

@Configuration
@Data
public class SocketClientConfig {

    private ConnectionPool pool;

    /*
     * 初始化一个Socket池
     *
     * */
    @Bean
    public void createPool(){
        this.pool = new ConnectionPool(new SocketFactory());
    }

}

连接服务端

当客户端启动时就会尝试连接服务端,如果连接成功,后续发送数据会以长连接输出流的形式发送数据,如果链接失败或断开连接,后续发送数据时会重新尝试连接成功后才会发送数据,所以不管重启服务端还是客户端都不影响数据的传输,除非有一方未启动

@Component
public class SocketClient implements CommandLineRunner {

    @Autowired
    private SocketClientConfig socketClientConfig;

    private Socket socket;


    @Override
    public void run(String... args) {
        getSocketStream();
    }

    public void getSocketStream() {
        //获取连接池
        GenericObjectPool<Socket> pool = socketClientConfig.getPool().getPool();
        //连接服务端
        try {
            socket = pool.borrowObject();
        } catch (Exception e) {
            System.out.println("socket连接失败");
        }
    }

    public void getMessage(String message) throws Exception {

        GenericObjectPool<Socket> pool = socketClientConfig.getPool().getPool();

        if (socket == null) {
            //重新尝试连接一下
            getSocketStream();
            if (socket == null) {
                System.out.println("消息发送失败");
                return;
            }
        }

        try {
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(message.getBytes());
        } catch (IOException e) {
            //获取流失败 可能已经断开连接了
            //关闭连接
            socket.close();
            //从池中移除对象
            pool.invalidateObject(socket);
            //将socket重置
            socket = null;
            //这里的socket被置空 所以会先走上面的重新连接 连接成功了才会走下面,并不会进入死循环
            getMessage(message);
        }
    }


}

测试

启动服务端

服务端socket启动成功

启动客户端

服务端socket启动成功
socket链接成功

创建测试接口

@RestController
@RequestMapping
public class SocketClientConller {


    @Autowired
    private SocketClient socketClient;

    @PostMapping("/test")
    public void test(@RequestParam String s) throws Exception {
        socketClient.getMessage(s);
    }
}

调用测试接口,发送内容:测试一下

服务端socket启动成功
socket链接成功
读取成功:测试一下

重启客户端

服务端socket启动成功
socket链接成功
读取成功:测试一下
链接关闭
服务端socket启动成功
socket链接成功

重启服务端并再次调用接口

服务端socket启动成功
socket链接成功
读取成功:测试一下

停止服务端并再次调用接口

socket连接失败
消息发送失败

总结

这个案例只是基于springBoot做了一个socket长连接的demo,仅作参考

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐