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

使用Socket发送Base64编码文件时遭遇TooLongFrameException问题求助

使用Socket发送Base64编码文件时遭遇TooLongFrameException问题求助

大家好,我正在用C#开发一个基于System.Net.Sockets.Socket的MIME格式邮件客户端,测试环境用的是Java编写的Test-Mail-Server。最近遇到一个棘手的问题:当我把文件转成Base64编码后通过Socket发送时,服务器抛出了TooLongFrameException,但直接发送文件原始字节却没问题,想请教下各位该怎么解决?

问题重现过程

我先建立Socket连接,依次发送SMTP命令(HELO、MAIL FROM、RCPT TO、DATA),然后读取本地docx文件转成Base64字符串发送,最后发送.r\n结束DATA阶段,代码如下:

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

client.Connect(IPAddress.Parse("127.0.0.1"), 2225);

client.Send(Encoding.ASCII.GetBytes("HELO test.com\r\n"));
client.Send(Encoding.ASCII.GetBytes("MAIL FROM: <abc@gmail.com>\r\n"));
client.Send(Encoding.ASCII.GetBytes("RCPT TO: <abc@gmail.com>\r\n"));
client.Send(Encoding.ASCII.GetBytes("DATA\r\n"));

byte[] data = File.ReadAllBytes(@"D:\test.docx");
string contentBase64 = Convert.ToBase64String(data);

client.Send(Encoding.ASCII.GetBytes(contentBase64));
client.Send(Encoding.ASCII.GetBytes(".\r\n"));

报错信息

服务器抛出的异常栈如下:

An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.

io.netty.handler.codec.TooLongFrameException: frame length (over 74240) exceeds the allowed maximum (16384)

at io.netty.handler.codec.LineBasedFrameDecoder.fail(LineBasedFrameDecoder.java:150)
at io.netty.handler.codec.LineBasedFrameDecoder.decode(LineBasedFrameDecoder.java:122)
at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:232)
at io.netty.handler.codec.DelimiterBasedFrameDecoder.decode(DelimiterBasedFrameDecoder.java:216)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:647)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:582)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:499)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:461)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at java.lang.Thread.run(Unknown Source)

已尝试的解决方案

我试过Copilot和ChatGPT给出的分块读取文件转Base64再合并发送的方法,但还是报同样的错,代码如下:

static void SendFile(NetworkStream stream, string filePath)
{
    byte[] buffer = new byte[4096];
    int bytesRead;
    using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
    using (MemoryStream memoryStream = new MemoryStream())
    {
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            // Convert the chunk to Base64 and write it to the memory stream
            string base64Chunk = Convert.ToBase64String(buffer, 0, bytesRead);
            byte[] base64Bytes = Encoding.ASCII.GetBytes(base64Chunk);
            memoryStream.Write(base64Bytes, 0, base64Bytes.Length);
        }
        // Send the entire Base64 content in one go
        stream.Write(memoryStream.ToArray(), 0, (int)memoryStream.Length);
    }
    Console.WriteLine("File sent successfully.");
}

我的疑问

为什么直接发文件字节没问题,转成Base64就报错?分块转码的思路哪里错了?应该怎么修改才能让服务器正常接收Base64编码的文件?


解决方案(社区解答)

这个问题的核心原因是服务器使用了Netty的LineBasedFrameDecoder按行解析数据,而你生成的Base64字符串是一整段超长内容,没有换行,超过了解码器默认的16384字节帧长度限制。而直接发送原始文件字节时,文件内容里可能存在自然换行,刚好符合解码器的行解析规则,所以没报错。

针对这个问题,有两个关键修复点:

1. 给Base64编码内容添加换行符(符合MIME规范)

根据RFC 2045的规定,Base64编码的内容应该每76个字符插入一个\r\n换行。C#的Convert.ToBase64String有一个重载可以自动处理这个:使用Base64FormattingOptions.InsertLineBreaks参数,生成带换行的Base64字符串,这样每一行的长度就不会超过服务器的帧限制。

修改后的Base64生成代码:

string contentBase64 = Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks);

2. 完善MIME邮件格式(可选但重要)

你当前的DATA阶段只发送了Base64内容,缺少必要的MIME头部信息,服务器可能无法正确识别这是一个附件。建议在发送Base64内容前,先发送MIME头部:

// 发送MIME邮件头部
string mimeHeaders = "Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name=\"test.docx\"\r\n" +
                     "Content-Transfer-Encoding: base64\r\n" +
                     "Content-Disposition: attachment; filename=\"test.docx\"\r\n\r\n";
client.Send(Encoding.ASCII.GetBytes(mimeHeaders));

3. 优化发送流程(可选)

另外,发送SMTP命令后最好读取服务器的响应(比如发送HELO后读取返回的250状态码),确保前一个命令执行成功再发送下一个,避免因服务器状态异常导致的隐藏问题。

最终修改后的完整代码示例

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(IPAddress.Parse("127.0.0.1"), 2225);

// 发送SMTP命令并读取响应(示例简化,实际应处理响应)
client.Send(Encoding.ASCII.GetBytes("HELO test.com\r\n"));
// 此处可添加读取服务器响应的代码
client.Send(Encoding.ASCII.GetBytes("MAIL FROM: <abc@gmail.com>\r\n"));
// 此处可添加读取服务器响应的代码
client.Send(Encoding.ASCII.GetBytes("RCPT TO: <abc@gmail.com>\r\n"));
// 此处可添加读取服务器响应的代码
client.Send(Encoding.ASCII.GetBytes("DATA\r\n"));
// 此处可添加读取服务器响应的代码

// 发送MIME头部
string mimeHeaders = "Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name=\"test.docx\"\r\n" +
                     "Content-Transfer-Encoding: base64\r\n" +
                     "Content-Disposition: attachment; filename=\"test.docx\"\r\n\r\n";
client.Send(Encoding.ASCII.GetBytes(mimeHeaders));

// 读取文件并生成带换行的Base64
byte[] data = File.ReadAllBytes(@"D:\test.docx");
string contentBase64 = Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks);
client.Send(Encoding.ASCII.GetBytes(contentBase64));

// 结束DATA阶段(注意前面加换行,确保.单独一行)
client.Send(Encoding.ASCII.GetBytes("\r\n.\r\n"));
// 此处可添加读取服务器响应的代码

备注:内容来源于stack exchange,提问作者Do Tan Ngoc Anh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 07:53:01