当前位置: 首页 > 工具软件 > smart-socket > 使用案例 >

smart-socket如何实现字符串通信

施默
2023-12-01

不少的朋友在接触到smart-socket后,一上来就想写一个字符串通信的案例。例如客户端发送"Hello Server!",服务端接收到该消息并响应"Hi Client!"。但最终发现客户端可以成功将消息发送出去,但服务端就是无法正常接收。那是不是这个smart-socket框架不行?为什么原先用Netty很简单就实现了?

也许您是第一次接触通信,又或者此前有过一些通信编程经历,但只要无法用smart-socket写出一个简单的字符串通信示例,那说明您的通信之旅还未入门。本文尽量给大家解释清楚通信编程的常见套路,此后大家可以轻松玩转socket。

我们以两个人的对话场景为例,A对B说:"我叫A我今年3岁"。从这句话中,我们很容易解读出A对B传递了两个消息,一个是“我叫A”,另一个是“我今年3岁”。人为识别一句话是一件很自然的事,我们甚至不用去思考为什么我们可以从一句话中解读出多个消息,那是一种本能。在这个本能的背后有一套非常复杂的“算法”在帮你准确的识别并分析日常交流的信息。但是在计算机要模拟现实世界的信息通信场景,就需要由程序员为机器赋予消息识别能力,这个能力也是通信中非常重要的环节:信息编解码。

同样是“我叫A我今年3岁”,人可以通过逐个文字读取,结合上下文的语义分析识别,最终解析出两个消息,而将这套算法转换成机器算法却是一件非常困难的事。不过我们可以采用机器更容易接受的方式来实现这个功能,接下里给大家介绍两种。

方案一

首先,两个通信机器可以事先约定好在每一个消息之前,先告诉对方这个消息长度多少,这样在传输的过程中无论一次性传递多少个消息,对方都能准确识别出。例如机器A与机器B的通信方式可以是这样的。A告知B:“这个消息3个字,我叫A,这个消息5个字,我今年3岁”,网络传输的形态就是“3我叫A5我今年3岁”。机器B收到这串内容之后就很容易解析出A传递的两个消息,只要先识别出即将解析消息的长度,再读取指定长度的数据便是一个完整的消息。

方案二

机器A可以与机器B约定好“接下来我的消息都以某个分割符为标志,且这个分隔符肯定不会出现于消息内容中的”。比如双方约定好的分隔符为逗号,那么双方通信的内容本身必然不能出现该符号。此时机器A传输给B的数据为“我叫A,我今年3岁,”,机器B通过分隔符便识别出了两个消息。

总之大家要谨记两点:

  1. 你的程序没有识别白话文的能力;
  2. 绝大部分的通信编解码都可按照本文提供的两种方案处理。

如果您的悟性还不错的话,文章看到此处便完结了。如果本文解释的还不够清楚,那接下来用代码来给大家示范方案一的实现。

  1. 约定双方通信的编解码协议
/**
 * @author 三刀
 * @version V1.0 , 2018/8/25
 */
public class StringProtocol implements Protocol<String> {

    private static final int INT_LENGTH = 4;

    @Override
    public String decode(ByteBuffer readBuffer, AioSession<String> session, boolean eof) {
        //识别消息长度
        if (readBuffer.remaining() < INT_LENGTH) {
            return null;
        }
        //判断是否存在半包情况
        int len = readBuffer.getInt(0);
        if (readBuffer.remaining() < len) {
            return null;
        }
        readBuffer.getInt();//跳过length字段
        byte[] bytes = new byte[len - INT_LENGTH];
        readBuffer.get(bytes);
        return new String(bytes);
    }

    @Override
    public ByteBuffer encode(String msg, AioSession<String> session) {
        byte[] bytes = msg.getBytes();
        ByteBuffer buffer = ByteBuffer.allocate(INT_LENGTH + bytes.length);
        buffer.putInt(INT_LENGTH + bytes.length);
        buffer.put(bytes);
        buffer.flip();
        return buffer;
    }
}
  1. 服务端实现消息处理逻辑并启动服务
/**
 * @author 三刀
 * @version V1.0 , 2018/8/25
 */
public class StringServerProcessor implements MessageProcessor<String> {
    public static void main(String[] args) throws IOException {
        AioQuickServer<String> server = new AioQuickServer<>(8080, new StringProtocol(), new StringServerProcessor());
        server.start();
    }

    @Override
    public void process(AioSession<String> session, String msg) {
        System.out.println("收到客户端消息:" + msg);
        try {
            session.write("服务端收到了你的消息:" + msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stateEvent(AioSession<String> session, StateMachineEnum stateMachineEnum, Throwable throwable) {

    }
}
  1. 客户端发送消息并接受服务的响应
/**
 * @author 三刀
 * @version V1.0 , 2018/8/25
 */
public class StringClientProcessor implements MessageProcessor<String> {
    private AioSession<String> session;

    public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
        StringClientProcessor processor = new StringClientProcessor();
        AioQuickClient<String> client = new AioQuickClient<>("localhost", 8080, new StringProtocol(), processor);
        client.start();
        int i = 0;
        while (i++ < 10) {
            processor.getSession().write("Hello:" + i);
        }
        Thread.sleep(1000);
        client.shutdown();
    }

    @Override
    public void process(AioSession<String> session, String msg) {
        System.out.println("收到服务端消息:" + msg);
    }

    @Override
    public void stateEvent(AioSession<String> session, StateMachineEnum stateMachineEnum, Throwable throwable) {
        if (stateMachineEnum == StateMachineEnum.NEW_SESSION) {
            this.session = session;
        }
    }

    public AioSession<String> getSession() {
        return session;
    }
}
 类似资料: