zoukankan      html  css  js  c++  java
  • c#TCP传输文件

    TCP是一种面向连接的,可靠的,基于字节流的传输层通信协议。TCP建立一个连接需要三次握手,而终止一个连接要经过四次握手。一旦通信双方建立了TCP连接,连接中的任何一方都能向对方发送数据和接受对方发来的数据。TCP协议负责把用户数据(字节流)按一定的格式和长度组成多个数据报进行发送,并在接收到数据报之后按分解顺序重新组装和恢复传输的数据。

    使用TCP传输文件,可以直接使用socket进行传输,也可以使用TcpLister类和TcpClient类进行传输。其实TcpLister和TcpClient就是Socket封装后的类,是.NET为了简化编程复杂度而对套接字又进行了封装。但是,TcpLister和TcpClient只支持标准协议编程。如果希望编写非标准协议的程序,只能使用套接字socket来实现。

    下面分别讲解两种方法进行文件传输:

    因为和一些终端进行文件传输时,受发送缓冲区最大发送字节的影响,我这里每次发送512字节,循环发送,直到把文件传输完,然后关闭连接;接收文件时,同样是每次接收512字节,然后写入文件,当所有的数据都接收完时,最后关闭连接。

    当然,最后一次发送和接收的数据,以实际计算的数据大小来发送或者接收,不会是512字节,以免造成数据空白。

    一、直接使用socket进行文件传输

    服务端和发送端Demo界面分别如图1、2所示:

    图1 服务端界面图

    图2 客户端界面图

    1、服务器端代码如下:

            public ShakeHands()
            {
                InitializeComponent();
                IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
                txtIp.Text = ips[1].ToString();
                int port = 50001;
                txtPort.Text = port.ToString();
                ListBox.CheckForIllegalCrossThreadCalls = false;//关闭跨线程对ListBox的检查
                
            }
    
            
    
            #region 启动TCP监听服务,开始接收连接和文件
            private void btnBegin_Click(object sender, EventArgs e)
            {
                try
                {
                    ReceiveFiles.BeginListening(txtIp.Text, txtPort.Text, lstbxMsgView, listbOnline);
                    btnBegin.Enabled = false;
                    btnCancel.Enabled = true;
                }
                catch (Exception ex)
                {
                    ShwMsgForView.ShwMsgforView(lstbxMsgView, "监听服务器出现了错误:"+ex.Message);
                }
            }
            #endregion

    其中,启动监听,接收文件ReceiveFiles类代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Net.Sockets;
    using System.Net;
    using System.Windows.Forms;
    using System.IO;
    
    
    namespace BusinessLogicLayer
    {
        public class ReceiveFiles
        {
            private static Thread threadWatch = null;
            private static Socket socketWatch = null;
            private static ListBox lstbxMsgView;//显示接受的文件等信息
            private static ListBox listbOnline;//显示用户连接列表
    
            private static Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
            /// <summary>
            /// 开始监听
            /// </summary>
            /// <param name="localIp"></param>
            /// <param name="localPort"></param>
            public static void BeginListening(string localIp, string localPort, ListBox listbox, ListBox listboxOnline)
            {
                //基本参数初始化
                lstbxMsgView = listbox;
                listbOnline = listboxOnline;
    
                //创建服务端负责监听的套接字,参数(使用IPV4协议,使用流式连接,使用Tcp协议传输数据)
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //获取Ip地址对象
                IPAddress address = IPAddress.Parse(localIp);
                //创建包含Ip和port的网络节点对象
                IPEndPoint endpoint = new IPEndPoint(address, int.Parse(localPort));
                //将负责监听的套接字绑定到唯一的Ip和端口上
                socketWatch.Bind(endpoint);
                //设置监听队列的长度
                socketWatch.Listen(10);
                //创建负责监听的线程,并传入监听方法
                threadWatch = new Thread(WatchConnecting);
                threadWatch.IsBackground = true;//设置为后台线程
                threadWatch.Start();//开始线程
                //ShowMgs("服务器启动监听成功");
                ShwMsgForView.ShwMsgforView(lstbxMsgView, "服务器启动监听成功");
            }
    
            /// <summary>
            /// 连接客户端
            /// </summary>
            private static void WatchConnecting()
            {
                while (true)//持续不断的监听客户端的请求
                {
                    //开始监听 客户端连接请求,注意:Accept方法,会阻断当前的线程
                    Socket connection = socketWatch.Accept();
                    if (connection.Connected)
                    {
                        //向列表控件中添加一个客户端的Ip和端口,作为发送时客户的唯一标识
                        listbOnline.Items.Add(connection.RemoteEndPoint.ToString());
                        //将与客户端通信的套接字对象connection添加到键值对集合中,并以客户端Ip做为健
                        dict.Add(connection.RemoteEndPoint.ToString(), connection);
    
                        //创建通信线程
                        ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);
                        Thread thradRecMsg = new Thread(pts);
                        thradRecMsg.IsBackground = true;
                        thradRecMsg.Start(connection);
                        ShwMsgForView.ShwMsgforView(lstbxMsgView, "客户端连接成功" + connection.RemoteEndPoint.ToString());
                    }
                }
            }
    
            /// <summary>
            /// 接收消息
            /// </summary>
            /// <param name="socketClientPara"></param>
            private static void RecMsg(object socketClientPara)
            {
                Socket socketClient = socketClientPara as Socket;
    
                while (true)
                {
                    //定义一个接受用的缓存区(100M字节数组)
                    //byte[] arrMsgRec = new byte[1024 * 1024 * 100];
                    //将接收到的数据存入arrMsgRec数组,并返回真正接受到的数据的长度   
                    if (socketClient.Connected)
                    {
                        try
                        {
                            //因为终端每次发送文件的最大缓冲区是512字节,所以每次接收也是定义为512字节
                            byte[] buffer = new byte[512];
                            int size = 0;
                            long len = 0;
                            string fileSavePath = @"..\..\files";//获得用户保存文件的路径
                            if (!Directory.Exists(fileSavePath))
                            {
                                Directory.CreateDirectory(fileSavePath);
                            }
                            string fileName = fileSavePath + "\\" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".doc";
                            //创建文件流,然后让文件流来根据路径创建一个文件
                            FileStream fs = new FileStream(fileName, FileMode.Create);
                            //从终端不停的接受数据,然后写入文件里面,只到接受到的数据为0为止,则中断连接
    
                            DateTime oTimeBegin = DateTime.Now;
    
                            while ((size = socketClient.Receive(buffer, 0, buffer.Length, SocketFlags.None)) > 0)
                            {
                                fs.Write(buffer, 0, size);
                                len += size;
                            }
                            DateTime oTimeEnd = DateTime.Now;
                            TimeSpan oTime = oTimeEnd.Subtract(oTimeBegin);
                            fs.Flush();
                            ShwMsgForView.ShwMsgforView(lstbxMsgView,socketClient.RemoteEndPoint + "断开连接");
                            dict.Remove(socketClient.RemoteEndPoint.ToString());
                            listbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
                            socketClient.Close();
                            ShwMsgForView.ShwMsgforView(lstbxMsgView, "文件保存成功:" + fileName);
                            ShwMsgForView.ShwMsgforView(lstbxMsgView, "接收文件用时:" + oTime.ToString()+",文件大小:"+len/1024+"kb");
                        }
                        catch
                        {
                            ShwMsgForView.ShwMsgforView(lstbxMsgView, socketClient.RemoteEndPoint + "下线了");
                            dict.Remove(socketClient.RemoteEndPoint.ToString());
                            listbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
                            break;
                        }
                    }
                    else
                    {
    
                    }
                }
            }
    
            /// <summary>
            /// 关闭连接
            /// </summary>
            public static void CloseTcpSocket()
            {
                dict.Clear();
                listbOnline.Items.Clear();
                threadWatch.Abort();
                socketWatch.Close();
                ShwMsgForView.ShwMsgforView(lstbxMsgView, "服务器关闭监听");
            }
        }
    
    
    }

    显示时时动态信息ShwMsgForView类代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    
    namespace BusinessLogicLayer
    {
        public class ShwMsgForView
        {
            delegate void ShwMsgforViewCallBack(ListBox listbox, string text);
            public static void ShwMsgforView(ListBox listbox, string text)
            {
                if (listbox.InvokeRequired)
                {
                    ShwMsgforViewCallBack shwMsgforViewCallBack = ShwMsgforView;
                    listbox.Invoke(shwMsgforViewCallBack, new object[] { listbox, text });
                }
                else
                {
                    listbox.Items.Add(text);
                    listbox.SelectedIndex = listbox.Items.Count - 1;
                    listbox.ClearSelected();
                }
            }
        }
    }

    2、客户端发送文件代码

    首先连接服务器代码:

            #region 连接服务器
            private void btnBegin_Click(object sender, EventArgs e)
            {
                IPAddress address = IPAddress.Parse(txtIp.Text.Trim());
                IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
                //创建服务端负责监听的套接字,参数(使用IPV4协议,使用流式连接,使用TCO协议传输数据)
                socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socketClient.Connect(endpoint);
                if (socketClient.Connected)
                {
                    ShowMgs(socketClient.RemoteEndPoint +"连接成功");
                }
            }
            #endregion

    连接服务器成功后,即可发送文件了,先选择文件:

            #region 选择要发送的文件
            private void btnSelectFile_Click(object sender, EventArgs e)
            {
                OpenFileDialog ofd = new OpenFileDialog();
                if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    txtFileName.Text = ofd.FileName;
                }
            }
            #endregion

    发送文件代码:

            //使用socket向服务端发送文件
            private void btnSendFile_Click(object sender, EventArgs e)
            {
                int i = Net.SendFile(socketClient, txtFileName.Text,512,1);
                if (i == 0)
                {
                    ShowMgs(txtFileName.Text + "文件发送成功");
                    socketClient.Close();
                    ShowMgs("连接关闭");
                }
                else
                {
                    ShowMgs(txtFileName.Text + "文件发送失败,i="+i);
                }
                
            }

    其中,发送文件Net类的代码如下:

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.IO;
    
    namespace MyCharRoomClient
    {
        /// <summary>
        /// Net : 提供静态方法,对常用的网络操作进行封装
        /// </summary>
        public sealed class Net
        {
            private Net()
            {
            }
    
            /// <summary>
            /// 向远程主机发送数据
            /// </summary>
            /// <param name="socket">要发送数据且已经连接到远程主机的 Socket</param>
            /// <param name="buffer">待发送的数据</param>
            /// <param name="outTime">发送数据的超时时间,以秒为单位,可以精确到微秒</param>
            /// <returns>0:发送数据成功;-1:超时;-2:发送数据出现错误;-3:发送数据时出现异常</returns>
            /// <remarks >
            /// 当 outTime 指定为-1时,将一直等待直到有数据需要发送
            /// </remarks>
            public static int SendData(Socket socket, byte[] buffer, int outTime)
            {
                if (socket == null || socket.Connected == false)
                {
                    throw new ArgumentException("参数socket 为null,或者未连接到远程计算机");
                }
                if (buffer == null || buffer.Length == 0)
                {
                    throw new ArgumentException("参数buffer 为null ,或者长度为 0");
                }
    
                int flag = 0;
                try
                {
                    int left = buffer.Length;
                    int sndLen = 0;
    
                    while (true)
                    {
                        if ((socket.Poll(outTime * 100, SelectMode.SelectWrite) == true))
                        {        // 收集了足够多的传出数据后开始发送
                            sndLen = socket.Send(buffer, sndLen, left, SocketFlags.None);
                            left -= sndLen;
                            if (left == 0)
                            {                                        // 数据已经全部发送
                                flag = 0;
                                break;
                            }
                            else
                            {
                                if (sndLen > 0)
                                {                                    // 数据部分已经被发送
                                    continue;
                                }
                                else
                                {                                                // 发送数据发生错误
                                    flag = -2;
                                    break;
                                }
                            }
                        }
                        else
                        {                                                        // 超时退出
                            flag = -1;
                            break;
                        }
                    }
                }
                catch (SocketException e)
                {
    
                    flag = -3;
                }
                return flag;
            }
    
    
            /// <summary>
            /// 向远程主机发送文件
            /// </summary>
            /// <param name="socket" >要发送数据且已经连接到远程主机的 socket</param>
            /// <param name="fileName">待发送的文件名称</param>
            /// <param name="maxBufferLength">文件发送时的缓冲区大小</param>
            /// <param name="outTime">发送缓冲区中的数据的超时时间</param>
            /// <returns>0:发送文件成功;-1:超时;-2:发送文件出现错误;-3:发送文件出现异常;-4:读取待发送文件发生错误</returns>
            /// <remarks >
            /// 当 outTime 指定为-1时,将一直等待直到有数据需要发送
            /// </remarks>
            public static int SendFile(Socket socket, string fileName, int maxBufferLength, int outTime)
            {
                if (fileName == null || maxBufferLength <= 0)
                {
                    throw new ArgumentException("待发送的文件名称为空或发送缓冲区的大小设置不正确.");
                }
                int flag = 0;
                try
                {
                    FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    long fileLen = fs.Length;                        // 文件长度
                    long leftLen = fileLen;                            // 未读取部分
                    int readLen = 0;                                // 已读取部分
                    byte[] buffer = null;
    
                    if (fileLen <= maxBufferLength)
                    {            /* 文件可以一次读取*/
                        buffer = new byte[fileLen];
                        readLen = fs.Read(buffer, 0, (int)fileLen);
                        flag = SendData(socket, buffer, outTime);
                    }
                    else
                    {
                        /* 循环读取文件,并发送 */
    
                        while (leftLen != 0)
                        {
                            if (leftLen < maxBufferLength)
                            {
                                buffer = new byte[leftLen];
                                readLen = fs.Read(buffer, 0, Convert.ToInt32(leftLen));
                            }
                            else
                            {
                                buffer = new byte[maxBufferLength];
                                readLen = fs.Read(buffer, 0, maxBufferLength);
                            }
                            if ((flag = SendData(socket, buffer, outTime)) < 0)
                            {
                                break;
                            }
                            leftLen -= readLen;
                        }
                    }
                    fs.Flush();
                    fs.Close();
                }
                catch (IOException e)
                {
    
                    flag = -4;
                }
                return flag;
            }
    
        }
    }

    这样,就可以进行文件的传输了,效果图如图3所示

    图3 文件传输效果图

     

    二、使用TcpLister和TcpClient进行文件传输

    TcpLister和TcpClient进行文件传输相对来说就要简单些,服务器Demo界面如图4所示:

    图4 服务器界面图

    启动监听和接收文件的代码如下:

    
    
    TcpListener listener;
    

    #region
    服务器启动监听服务,并开始接收文件 private void btnBegin_Click(object sender, EventArgs e) { btnBegin.Enabled = false; listener = new TcpListener(IPAddress.Parse(txtIp.Text), int.Parse(txtPort.Text)); listener.Start(); ShwMsgForView.ShwMsgforView(lstbxMsgView, "服务器开始监听"); Thread th = new Thread(ReceiveMsg); th.Start(); th.IsBackground = true; } public void ReceiveMsg() { while (true) { try { int size = 0; int len = 0; TcpClient client = listener.AcceptTcpClient(); if (client.Connected) { //向列表控件中添加一个客户端的Ip和端口,作为发送时客户的唯一标识 listbOnline.Items.Add(client.Client.RemoteEndPoint); ShwMsgForView.ShwMsgforView(lstbxMsgView, "客户端连接成功" + client.Client.RemoteEndPoint.ToString()); } NetworkStream stream = client.GetStream(); if (stream != null) { SaveFileDialog sfd = new SaveFileDialog(); if (sfd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { string fileSavePath = sfd.FileName;//获得用户保存文件的路径 FileStream fs = new FileStream(fileSavePath, FileMode.Create, FileAccess.Write); byte[] buffer = new byte[512]; while ((size = stream.Read(buffer, 0, buffer.Length)) > 0) { fs.Write(buffer, 0, size); len += size; } fs.Flush(); stream.Flush(); stream.Close(); client.Close(); ShwMsgForView.ShwMsgforView(lstbxMsgView, "文件接受成功" + fileSavePath); } } } catch(Exception ex) { ShwMsgForView.ShwMsgforView(lstbxMsgView, "出现异常:" + ex.Message); } } } #endregion

    客户端选择文件后,即可直接发送文件:

    客户端代码如下:

            //使用TcpLister和TcpClient向服务端发送文件
            private void button1_Click(object sender, EventArgs e)
            {
                TcpClient client = new TcpClient();
               
                client.Connect(IPAddress.Parse(txtIp.Text), int.Parse(txtPort.Text));
                NetworkStream ns = client.GetStream();
                FileStream fs = new FileStream(txtFileName.Text, FileMode.Open);
                int size = 0;//初始化读取的流量为0   
                long len = 0;//初始化已经读取的流量   
                while (len < fs.Length)
                {
                    byte[] buffer = new byte[512];
                    size = fs.Read(buffer, 0, buffer.Length);
                    ns.Write(buffer, 0, size);
                    len += size;
                    //Pro((long)len);   
                }
                fs.Flush();
                ns.Flush();
                fs.Close();
                ns.Close();
                ShowMgs(txtFileName.Text + "文件发送成功");
            }

    其中发送文件效果图如图5所示:

    图5 发送文件效果图


     

  • 相关阅读:
    10款免费开源图表插件推荐
    jQuery中$.get()、$.post()和$.ajax()
    layer弹出层的iframe页面回调
    【转载】MSXML应用总结 概念篇
    【转载】MSXML应用总结 开发篇(上)
    【转载】MSXML应用总结 开发篇(下)
    【转载】MFC怎么封装CreateWindow
    【转载】MFC的Main函数跑哪去了
    【转载】C++创建对象的两种方法
    【转载】malloc内存分配与free内存释放的原理
  • 原文地址:https://www.cnblogs.com/bianlan/p/2632349.html
Copyright © 2011-2022 走看看