You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在D语言中如何从socket获取输入并转换为UTF-8编码字符串

实现步骤

你当前使用的是D语言标准库的套接字API,要实现UTF-8编码的SQL请求字节转字符串,按如下逻辑补全代码即可:

  • 第一步:补全Socket.select后的事件分发逻辑,区分新连接接入、已连接套接字可读两类事件
  • 第二步:为每个连接单独维护接收缓冲区,解决TCP流的粘包问题
  • 第三步:按提前约定的请求边界规则提取完整请求字节,验证编码合法性后转为字符串

注意:TCP是无消息边界的流协议,你需要提前和客户端约定请求边界规则,常用两种方案:

  1. 长度前缀方案:每个请求前加2/4字节的大端整数,标识后续SQL请求的字节长度
  2. 分隔符方案:用客户端不会在SQL中使用的特殊字符(如\0、;\n)作为请求结束标识

补全后的代码示例

import std.socket;
import std.stdio;
import std.algorithm;
import std.array;
import std.utf;

int start(){
    ushort port = 61888;
    auto listener = new TcpSocket();
    assert(listener.isAlive);
    listener.blocking = false;
    listener.bind(new InternetAddress(port));
    listener.listen(10);
    writefln("Listening on port %d.", port);

    enum MAX_CONNECTIONS = 60;
    auto socketSet = new SocketSet(MAX_CONNECTIONS + 1);
    Socket[] reads;
    // 每个连接独立的接收缓冲区,解决粘包
    ubyte[][Socket] recvBuffers;

    while (true)
    {
        socketSet.add(listener);
        foreach (sock; reads)
            socketSet.add(sock);

        // 阻塞等待IO事件
        Socket.select(socketSet, null, null);

        // 处理新连接
        if (socketSet.isSet(listener)) {
            auto newSock = listener.accept();
            newSock.blocking = false;
            if (reads.length < MAX_CONNECTIONS) {
                reads ~= newSock;
                recvBuffers[newSock] = [];
            }
            socketSet.remove(listener);
        }

        // 处理已连接套接字的可读事件
        foreach (i, sock; reads) {
            if (!socketSet.isSet(sock)) continue;

            ubyte[4096] recvBuf;
            auto recvLen = sock.receive(recvBuf[]);

            // 连接关闭或出错
            if (recvLen <= 0) {
                sock.close();
                recvBuffers.remove(sock);
                reads = reads.remove(i);
                continue;
            }

            // 追加新接收的字节到对应连接的缓冲区
            recvBuffers[sock] ~= recvBuf[0..recvLen];

            // 示例采用【2字节大端长度前缀】的边界规则,你可以根据自己的协议修改
            while (recvBuffers[sock].length >= 2) {
                // 读取前2字节的请求长度
                ushort reqByteLen = (cast(ushort)recvBuffers[sock][0] << 8) | recvBuffers[sock][1];
                // 缓冲数据不足完整请求,等待下一轮接收
                if (recvBuffers[sock].length < 2 + reqByteLen) break;

                // 提取完整请求字节
                auto reqBytes = recvBuffers[sock][2 .. 2 + reqByteLen];
                // 剩余字节放回缓冲
                recvBuffers[sock] = recvBuffers[sock][2 + reqByteLen .. $];

                // 验证UTF-8编码合法性后转字符串
                try {
                    validate(cast(char[])reqBytes);
                    string sqlStr = cast(string)reqBytes.idup;
                    // 此处可添加你的SQL处理逻辑
                    writefln("接收到SQL请求:%s", sqlStr);
                } catch (UTFException e) {
                    writefln("非法UTF-8编码,断开客户端 %s", sock.remoteAddress.toString());
                    sock.close();
                    recvBuffers.remove(sock);
                    reads = reads.remove(i);
                    break;
                }
            }
        }
        // 清空套接字集合,准备下一轮循环
        socketSet.reset();
    }

    return 0;
}

边界规则调整说明

如果你采用分隔符方案(比如约定\0为请求结束符),只需要把上述代码中提取完整请求的逻辑替换为如下内容即可:

// 查找分隔符位置
auto delimiterPos = recvBuffers[sock].countUntil(0);
while (delimiterPos != -1) {
    // 提取分隔符前的字节作为完整请求
    auto reqBytes = recvBuffers[sock][0 .. delimiterPos];
    // 剩余字节跳过分隔符后放回缓冲
    recvBuffers[sock] = recvBuffers[sock][delimiterPos + 1 .. $];
    
    // 后续转UTF-8字符串的逻辑和上述一致
    // ...
    
    // 查找下一个分隔符
    delimiterPos = recvBuffers[sock].countUntil(0);
}

内容的提问来源于stack exchange,提问作者Tannenfels

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 07:57:01