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

自定义Socket封装连接Whois服务器时读取前Socket意外关闭的异常问题

问题根源分析

你的问题出在自定义SocketClient的协议逻辑和Whois协议不匹配,以及对Socket连接关闭时机的错误处理上:

  1. 错误的协议适配
    Whois是一个简单的纯文本协议:客户端发送域名\r\n后,服务器返回对应的Whois信息,然后主动关闭连接。但你的SocketClient却在发送数据时添加了4字节的长度头部(HEADER_SIZE = 4),接收时先读取这个头部来计算消息体长度——这是你自定义的私有协议逻辑,Whois服务器完全不识别这个头部,导致:
  • 服务器收到包含多余头部的错误请求后,可能直接关闭连接,触发你读取时的异常
  • 即使服务器返回了内容,你先读取的4字节其实是返回文本的前4个字符,转成整数后计算出的bodyLength会远大于实际返回的内容长度,后续读取时服务器已经关闭连接,自然抛出Tried to read from closed socket
  1. 对连接关闭的错误判定
    Whois服务器返回完数据后主动关闭连接是正常行为,但你的ReadStream方法只要读到bytesReceived == 0就抛出异常,这把正常的连接关闭当成了错误,这也是异常的来源之一。
解决方案

针对Whois协议的特性,修改你的SocketClient和WhoisResolver逻辑:

1. 修改SocketClient适配Whois协议

去掉自定义的长度头部逻辑,改为按照纯文本/字节流的方式收发数据:

public class SocketClient : ISocketClient
{
    private Socket _socket;

    public SocketClient(Socket socket)
    {
        _socket = socket;
    }

    public virtual Stream Connect(IPEndPoint endPoint)
    {
        _socket.Connect(endPoint);
        return new NetworkStream(_socket);
    }

    // 直接发送原始字符串,不需要添加头部
    public void Send(Stream networkStream, string message, Encoding encoding)
    {
        var bodyBytes = encoding.GetBytes(message);
        networkStream.Write(bodyBytes, 0, bodyBytes.Length);
        // 确保数据立即发送(可选,但Whois建议这样做)
        networkStream.Flush();
    }

    // 读取直到连接关闭,因为Whois返回完就关连接
    public string Receive(Stream networkStream)
    {
        using var ms = new MemoryStream();
        var buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, bytesRead);
        }
        // 连接正常关闭,不是错误,返回读取到的内容
        return Encoding.UTF8.GetString(ms.ToArray());
    }

    public void Disconnect(bool reuseSocket)
    {
        _socket.Disconnect(reuseSocket);
        _socket.Close();
    }
}

2. 调整WhoisResolver的异常处理

确保正确处理连接关闭的情况,同时保持现有结构:

public class WhoisResolver : IWhoisResolver
{
    private ISocketClient _socketClient;

    public WhoisResolver(ISocketClient socketClient)
    {
        _socketClient = socketClient;
    }

    public string? Lookup(string fullDomainName, string whoisServerAddress, int whoisServerPort = 43)
    {
        try
        {
            var ip = Dns.GetHostAddresses(whoisServerAddress).First();
            using var stream = _socketClient.Connect(new IPEndPoint(ip, whoisServerPort));
            _socketClient.Send(stream, $"{fullDomainName}\r\n", Encoding.ASCII);
            var response = _socketClient.Receive(stream);
            _socketClient.Disconnect(false);
            return response;
        }
        catch (SocketException ex)
        {
            // 可以在这里记录日志,区分是连接错误还是正常关闭
            Console.WriteLine($"Socket异常: {ex.Message}");
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"其他异常: {ex.Message}");
            return null;
        }
    }
}

3. 额外优化建议

  • 给SocketClient实现IDisposable接口,确保Socket和Stream能被正确释放,避免资源泄漏
  • 使用using语句管理SocketClient实例,比如:
using var socketClient = new SocketClient(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp));
var resolver = new WhoisResolver(socketClient);
var result = resolver.Lookup($"{domainName}.{tld}", whoisServer);

这样修改后,你的代码就完全适配Whois协议的工作方式了,不会再出现随机异常。

内容的提问来源于stack exchange,提问作者Rafał Mierzejewski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 21:58:13