随笔 - 233  文章 - 0  评论 - 246  阅读 - 84万

如何通过Socket TCP发送并接收一个文件?

一.小结

1.大包发小包收,只发一次。

2.发时把文件扩展名,文件长度也随同一起发送,方便接收端接收时另存为正确的文件类型,并判断是否已经接收完毕。

   如果不一起发送,分为文件扩展名,文件长度,文件内容,发送三次,在接收端,也可能会一起收到,反而不利于解析。

 

二.客户发送端代码

 

复制代码
        private void btnSend_Click(object sender, EventArgs e)
        {
            //组合出远程终结点  
            IPAddress ipAddress = IPAddress.Parse(this.txtIP3.Text);
            IPEndPoint hostEP = new IPEndPoint(ipAddress, Convert.ToInt32(this.txtPort3.Text));
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                socket.Connect(hostEP);

                //1.发送用户协议
                string path1 = Environment.CurrentDirectory; //获取应用程序的当前工作目录。
                string doc = "YourSendFile.pdf";
 string path = Path.Combine(path1, doc);
                FileStream fs = File.Open(path, FileMode.Open);

                //文件内容
                byte[] bdata = new byte[fs.Length];
                fs.Read(bdata, 0, bdata.Length);
                fs.Close();
                
                //文件扩展名,固定3字节
                byte[] fileExtArray = Encoding.UTF8.GetBytes(string.Format("{0:D3}", currentDocExt));

                //文件长度, 固定为20字节,前面会自动补零
                byte[] fileLengthArray = Encoding.UTF8.GetBytes(bdata.Length.ToString("D20"));
                
                //合并byte数组
                byte[] fileArray = CombomBinaryArray(fileExtArray, fileLengthArray);

                //合并byte数组
                byte[] bdata1 = CombomBinaryArray(fileArray, bdata);

                //发文件长度+文件内容
                socket.Send(bdata1, bdata1.Length, 0);

                //2.接收
                //声明接收返回内容的字符串  
                string recvStr = "";

                //声明字节数组,一次接收数据的长度为 1024 字节  
                byte[] recvBytes = new byte[1024];

                //返回实际接收内容的字节数  
                int bytes = 0;

                //循环读取,直到接收完所有数据  
                while (true)
                {
                    bytes = socket.Receive(recvBytes, recvBytes.Length, 0);
                    //读取完成后退出循环  
                    if (bytes <= 0) break;

                    //将读取的字节数转换为字符串  
                    recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
                }              


                //禁用 Socket  
                socket.Shutdown(SocketShutdown.Both);

                //关闭 Socket  
                socket.Close();

                //... do some busness logic ...

            }
            catch (Exception e1)
            {
                throw e1;
            }
        }
复制代码

 

三.服务接收端代码

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Xml;
using System.Configuration;
using System.Windows.Forms;
using System.IO;

namespace ConsoleAppServer
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            
            bool Done = false;
            IPAddress ipAddress = IPAddress.Parse(ConfigurationSettings.AppSettings["IP"].ToString());
            IPEndPoint hostEP = new IPEndPoint(ipAddress, Convert.ToInt32(ConfigurationSettings.AppSettings["Port"]));
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(hostEP);
            socket.Listen(3);
            while (!Done)
            {
                Console.WriteLine("waiting for client");

                Socket client = socket.Accept();

                Console.WriteLine("connected client");

                //1.接收
                //声明字节数组,一次接收数据的长度为 1024 字节  
                byte[] recvBytes = new byte[1024];
                //返回实际接收内容的字节数  
                int bytes = 0;
                                
                int FileLength = 0; // 900866;
                int ReceivedLength = 0;

                //1.0 接收文件扩展名
                bytes = client.Receive(recvBytes, 3, 0);
                string fileExt = Encoding.UTF8.GetString(recvBytes, 0, bytes);

                string vFilePath = Environment.CurrentDirectory;
                string vFileName = vFilePath + "\\Tmp" + Guid.NewGuid().ToString() + "." + fileExt;

                //创建文件流,然后让文件流来根据路径创建一个文件
                FileStream fs = new FileStream(vFileName, FileMode.Create);

                //1.1 接收文件长度
                bytes = client.Receive(recvBytes, 20, 0);
                //将读取的字节数转换为字符串  
                string fileLength = Encoding.UTF8.GetString(recvBytes, 0, bytes);
                FileLength = Convert.ToInt32(fileLength);

                //1.2接收文件内容
                while (ReceivedLength < FileLength)
                {
                    bytes = client.Receive(recvBytes, recvBytes.Length, 0);
                    ReceivedLength += bytes;
                    fs.Write(recvBytes, 0, bytes);
                }

                fs.Flush();
                fs.Close();


                //... do some business logic ...   
               string returnData = SomeBusinessLogic();

                //2.发送
                byte[] bdata = Encoding.UTF8.GetBytes(returnData);
                client.Send(bdata, bdata.Length, 0);

                client.Close();
            }

            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
        }
        
    }
}
复制代码

 

posted on   BobLiu  阅读(31882)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

点击右上角即可分享
微信分享提示