C# 基于 Socket TCP 实现图片上传与保存实例

陈默
陈默
发布时间:2026-10-13 | 5人浏览过
原创

在 C# 开发中,有时需要绕过 HTTP 等高级协议,直接使用底层的 Socket TCP 进行二进制数据传输,例如上传和保存图片。本文将介绍如何通过 Socket 连接,将本地图片文件转换为字节流发送至服务器,并在服务端将其还原为图像文件保存。该方法涉及文件路径处理、二进制读写、网络套接字通信及内存流操作。

核心原理与准备条件

实现 Socket 图片上传的关键在于数据的结构化传输。由于 TCP 是面向流的协议,直接发送可能会遇到粘包或断包问题,但在简单的点对点场景中,通常采用“先传文件名长度/内容,再传文件大小/内容”的顺序来区分元数据和数据主体。

第1步:配置客户端连接环境

首先需要在客户端(Client)建立与服务器端的 TCP 连接。主要步骤包括解析服务器 IP 地址、创建 Socket 实例并绑定到特定的端口(如 8000)。

  • 入口:用户点击开始连接按钮。
  • 动作:
  • 定义 IPAddress 为本机回环地址 "127.0.0.1"(测试环境)或实际服务器 IP。
  • 实例化 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)。
  • 调用 Connect 方法尝试连接目标 IPEndPoint。
  • 重要参数:端口号需与服务端保持一致,默认示例为 8000。
  • 完成状态:若连接成功,clientSocket 处于已连接状态;若失败需捕获异常提示用户。
// 客户端连接代码片段
IPAddress ip = IPAddress.Parse("127.0.0.1");
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
    clientSocket.Connect(new IPEndPoint(ip, 8000));
} catch { MessageBox.Show("连接服务器失败"); return; }

第2步:选择文件并提取名称

用户需通过对话框选择本地图片文件。为了便于服务端识别,客户端应先发送文件的原始名称(不含扩展名或含扩展名),以便服务端生成对应的存储文件名。

  • 入口:用户点击“选择文件”按钮。
  • 动作:
  • 使用 OpenFileDialog 设置过滤条件,仅允许选择常见图像格式(*.bmp, *.gif, *.jpg, *.jpeg, *.png)。
  • 获取选中文件的完整路径 openFile.FileName。
  • 使用 Path.GetFileNameWithoutExtension(filename) 去除路径和后缀,得到纯文件名,编码为字节数组后发送。
  • 注意:确保文件名不包含特殊字符,避免文件系统错误。
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "图像文件(*.bmp;*.gif;*.jpg;*.jpeg;*.png)|*.bmp;*.gif;*.jpg;*.jpeg;*.png";
openFile.Multiselect = false;
if (openFile.ShowDialog() == DialogResult.OK)
{
    textBox2.Text = openFile.FileName;
    // 发送文件名(不带扩展名)
    byte[] msg = Encoding.Default.GetBytes(Path.GetFileNameWithoutExtension(openFile.FileName));
    clientSocket.Send(msg);
}

第3步:客户端读取并发送图片二进制数据

文件名的指令发出后,客户端需读取图片文件的二进制内容并通过 Socket 发送。对于小文件,可直接一次性读取;大文件建议使用分块传输以控制内存占用。

  • 入口:文件选择确认后。
  • 动作:
  • 创建 FileStream 以只读模式打开选中的图片文件。
  • 创建足够大的字节数组 fssize(大小为文件长度)。
  • 使用 BinaryReader 将文件内容读入字节数组。
  • 调用 clientSocket.Send(fssize) 发送数据。
  • 发送完成后,关闭文件流并通知 Socket 停止发送(Shutdown Send),最后关闭 Socket 连接。
  • 风险提示:直接读取整个文件到内存可能导致大文件溢出,生产环境建议分块发送并添加总长标识。
// 客户端发送文件数据片段
FileStream fs = new FileStream(openFile.FileName, FileMode.OpenOrCreate, FileAccess.Read);
byte[] fssize = new byte[fs.Length];
BinaryReader strread = new BinaryReader(fs);
strread.Read(fssize, 0, fssize.Length - 1);
clientSocket.Send(fssize);
fs.Close();
clientSocket.Shutdown(System.Net.Sockets.SocketShutdown.Send);
clientSocket.Close();

第4步:服务端监听与接收连接

服务端(Server)程序启动后,需在特定 IP 和端口上监听 incoming 连接。接收到客户端连接请求后,Accept 该连接并创建一个新的 Socket 对象用于与该客户端通信。

  • 入口:用户点击服务端“开始监听”按钮。
  • 动作:
  • 绑定 IPEndPoint(IP 和端口 8000)。
  • 调用 Bind() 和 Listen(10) 启动监听。
  • 调用 Accept() 阻塞等待直到有客户端连接。
  • 将返回的 Socket 赋值给 newSocket 用于后续收发。
// 服务端监听代码片段
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint iep = new IPEndPoint(ip, 8000);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(iep); 
serverSocket.Listen(10);
Socket newSocket = serverSocket.Accept(); // 假设有此逻辑,原文省略了Accept语句但使用了newSocket

第5步:服务端接收文件名并初始化存储流

连接建立后,服务端首先接收客户端发来的文件名数据。随后,创建一个 MemoryStream 用于暂存收到的图片二进制数据,避免频繁磁盘 I/O 提高速度。

  • 入口:newSocket 就绪后。
  • 动作:
  • 分配缓冲区 recvBytes(1024字节)。
  • 调用 Receive 获取文件名字节数 bytes。
  • 使用 Encoding.ASCII.GetString 解码文件名。
  • 在 UI 显示预览图占位符,并构建最终保存的文件名 name + ".jpg"。
  • 初始化 MemoryStream fs 和接收缓冲区 b。
byte[] recvBytes = new byte[1024];
int bytes = newSocket.Receive(recvBytes, recvBytes.Length, SocketFlags.None);
string name = Encoding.ASCII.GetString(recvBytes, 0, bytes);
textBox1.Text = name + ".jpg"; // 更新UI显示文件名
MemoryStream fs = new MemoryStream();
int got = 0;

第6步:服务端循环接收并保存图片

这是核心数据处理步骤。服务端在一个循环中不断从 newSocket 接收数据块,写入 MemoryStream,直到无更多数据可读为止。最后,将内存流转换为 Bitmap 对象,并在 UI 控件显示,同时保存到硬盘固定目录。

  • 入口:文件名接收完成后。
  • 动作:
  • 使用 while(true) 循环调用 newSocket.Receive(b)。
  • 每次接收到的数据量 got 写入 fs.Write(b, 0, got)。
  • 当 got <= 0 时跳出循环,表示客户端已完成发送并关闭了发送端。
  • 利用 new Bitmap(fs) 还原图像。
  • 调用 Img.Save 将图片保存至 D:\images\ 目录,格式指定为 JPEG。
  • 关闭内存流和 Socket 连接。
byte[] b = new byte[1024 * 4];
while (true)
{
    got = newSocket.Receive(b);
    if (got > 0) {
        fs.Write(b, 0, got);
    } else {
        break;
    }
}
Bitmap Img = new Bitmap(fs);
pictureBox1.Image = Img; // 在UI显示
string filename = name + ".jpg";
Img.Save(@"D:\images" + filename, ImageFormat.Jpeg); // 保存到硬盘
fs.Close();
newSocket.Shutdown(System.Net.Sockets.SocketShutdown.Receive);
newSocket.Close();

完成判断与注意事项

  • 功能验证:运行服务端程序,点击“开始监听”;运行客户端程序,点击“连接服务器”,然后选择一张图片上传。观察服务端是否弹出保存成功的提示,且 D:\images\ 目录下出现对应的 .jpg 文件,且 UI 中能正常显示图片。
  • 编码一致性:客户端发送文件名时使用 Encoding.Default 或 UTF8,服务端接收时必须使用相同的编码方式解码,否则中文文件名会出现乱码导致保存失败。
  • 异常处理:上述代码示例简化了异常捕获。在实际项目中,应确保即使传输中断,资源(Stream, Socket)也能被正确释放,建议使用 using 语句包裹资源。
  • 大文件优化:当前实现将所有图片载入内存再一次性保存,适合中小图片。若需传输大图,建议在前端先计算文件总长度并发送,后端根据总长度判断接收是否完整。

以上就是 C# 使用 Socket 上传并保存图片的详细内容,更多关于 C# Socket 编程与网络通信的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表