C# Socket服務端與客戶端通信(包含大文件的斷點傳輸)


C# Socket服務端與客戶端通信(包含大文件的斷點傳輸)

 

步驟:

一、服務端的建立

1.服務端的項目建立以及頁面布局

2.各功能按鍵的事件代碼

  1)傳輸類型說明以及全局變量

  2)Socket通信服務端具體步驟:

     (1)建立一個Socket

     (2)接收信息

     (3)發送數據(這里分發送字符串、文件(包含大文件)、震動)

二、客戶端的建立

1.服務端的項目建立以及頁面布局

2.各功能按鍵的事件代碼

  1)傳輸類型說明以及全局變量

  2)Socket通信服務端具體步驟:

     (1)建立一個Socket

     (2)接收信息

     (3)發送數據(這里分發送字符串、文件(包含大文件)、震動)

 

 

注意:此圖是Socket通信的精華,在使用Socket通信時,有什么迷惑的可以看看此圖,下面我們講解的時候也是參照此圖

 

Socket大家肯定很熟悉,對已內部的通信邏輯,肯定也有一定得了解---

對於Socket研究了兩天寫了一個小程序,通過Socket服務端與客戶端的通信,以及大文件之間斷點的傳輸(這里只做了服務端給客戶端傳送大文件,如果想把客戶端的大文件傳送給服務端也是一樣的道理,看了文章,大家肯定可以自己實現)······

(自己才疏學淺,如有bug請諒解,但功能還是能實現的)

下面根據步驟進入正題:

一、服務端的建立

1.服務端的項目建立以及頁面布局

新建解決方案“Socket通信”以及兩個Winform項目(1)SockeClient——客戶端    (2)SocketServer——服務器

給服務端界面布局——參照上圖(這個大家肯定都是手到擒來就不累贅了······)

2.各功能按鍵的事件代碼

先把整個服務端的代碼貼出來,然后我們在一一講解

namespace SocketServer
{
    public partial class Form1 : Form
    {

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息
        // 0:表示傳遞的是字符串信息
        // 1:表示傳遞的是文件信息
        // 2:表示的是震動

        /// <summary>
        /// 用來存放連接服務的客戶端的IP地址和端口號,對應的Socket
        /// </summary>
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //不檢測跨線程之間的空間調用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 開啟監聽
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket
                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取IP
                IPAddress ip = IPAddress.Any;
                //創建端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //監聽
                socketWatch.Bind(port);
                ShowMsg("監聽成功");
                socketWatch.Listen(10);
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start(socketWatch);
            }
            catch
            {
                
            }           
        }

        /// <summary>
        /// 接收客戶端發送的信息
        /// </summary>
        /// <param name="o"></param>
        private void AcceptMgs(object o)
        {
            try
            {
                Socket socketWatc = (Socket)o;
                while (true)
                {
                    ////負責跟客戶端通信的Socket
                    Socket socketSend = socketWatc.Accept();
                    //將遠程連接的客戶端的IP地址和Socket存入集合中
                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
                    //將遠程連接的客戶端的IP地址和端口號存儲下拉框中
                    cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());
                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功");
                    //新建線程循環接收客戶端發來的信息
                    Thread td = new Thread(Recive);
                    td.IsBackground = true;
                    td.Start(socketSend);
                }
            }
            catch { }
            
        }

        /// <summary>
        /// 接收客戶端發來的數據,並顯示出來
        /// </summary>
        private void Recive(object o)
        {
            Socket socketSend = (Socket)o;
            try
            {
                while (true)
                {
                    //客戶端連接成功后,服務器應該接受客戶端發來的消息
                    
                    if (socketSend == null)
                    {
                        MessageBox.Show("請選擇要發送的客戶端");
                        continue;
                    }
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //實際接受到的有效字節數
                    int r = socketSend.Receive(buffer);
                    //如果客戶端關閉,發送的數據就為空,然后就跳出循環
                    if (r == 0)
                    {
                        break;
                    }                   
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        string filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 1, r - 1);
                        }
                        ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch{}          
        }


        /// <summary>
        /// 顯示信息
        /// </summary>
        /// <param name="message"></param>
        private void ShowMsg(string message)
        {
            txtLog.AppendText(message + "\r\n");
        }

        /// <summary>
        /// 發送信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            
            //獲得選中客戶端ip對應的通信Socket       
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            string strSend=txtMsg.Text;
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(strSend);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換為數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch
            {
            }            
        }

        /// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

        /// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //判斷是否選擇了要發送的客戶端
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            string filePath = txtPath.Text;
            if (string.IsNullOrEmpty(filePath))
            {
                MessageBox.Show("請選擇文件");
                return;
            }
            Thread td = new Thread(SendBigFile);
            td.IsBackground = true;
            td.Start();
            
        }

        /// <summary>
        /// 大文件斷點傳送
        /// </summary>
        private void SendBigFile()
        {
            string filePath = txtPath.Text;
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            try
            {
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    //1. 第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件
                    long length = fsRead.Length;
                    byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(byteLength);
                    socketSend.Send(list.ToArray()); //
                    //2. 第二步:每次發送一個1MB的包,如果文件較大,則會拆分為多個包
                    byte[] buffer = new byte[1024 * 1024];
                    long send = 0; //發送的字節數                   
                    while (true)  //大文件斷點多次傳輸
                    {
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        if (r == 0)
                        {
                            break;
                        }
                        socketSend.Send(buffer, 0, r, SocketFlags.None);
                        send += r;
                        ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length));
                    }
                    ShowMsg("發送完成");
                    txtPath.Text = "";
                }
            }
            catch
            {

            }
        }

        /// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {
            //判斷是否選擇了要發送的客戶端
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch
            {
                
            }
            
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的坐標
            Point point = this.Location;
            //反復給窗體坐標復制一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }
    }
}

  

1)傳輸類型說明以及全局變量

這些說明以及全局變量,說的也比較清楚,也不累贅了。

2)Socket通信服務端具體步驟:

(這些步驟都是根據第一個圖來的)

 (1)建立一個Socket

/// <summary>
        /// 開啟監聽
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //當點擊開始監聽的時候 在服務器端創建一個負責監IP地址跟端口號的Socket
                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取IP
                IPAddress ip = IPAddress.Any;
                //創建端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //監聽
                socketWatch.Bind(port);
                ShowMsg("監聽成功");
                socketWatch.Listen(10);
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start(socketWatch);
            }
            catch
            {
                
            }           
        }

  

在開啟監聽按鈕里,我們建立了Socket,以及監聽的最大客戶端數 socketWatch.Listen(10)

由於服務端會不停的去監視接收客戶端發來的信息,如果把這個工作放到主線程里,程序會出現假死的現象,所以這里給他放到一個新的線程里。

(2)接收信息

/// <summary>
        /// 接收客戶端發送的信息
        /// </summary>
        /// <param name="o"></param>
        private void AcceptMgs(object o)
        {
            try
            {
                Socket socketWatc = (Socket)o;
                while (true)
                {
                    ////負責跟客戶端通信的Socket
                    Socket socketSend = socketWatc.Accept();
                    //將遠程連接的客戶端的IP地址和Socket存入集合中
                    dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
                    //將遠程連接的客戶端的IP地址和端口號存儲下拉框中
                    cboUsers.Items.Add(socketSend.RemoteEndPoint.ToString());
                    ShowMsg(socketSend.RemoteEndPoint.ToString() + ": 連接成功");
                    //新建線程循環接收客戶端發來的信息
                    Thread td = new Thread(Recive);
                    td.IsBackground = true;
                    td.Start(socketSend);
                }
            }
            catch { }
            
        }

  

接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什么

這個在方法Recive里進行判斷

/// <summary>
namespace SocketClient
{
    public partial class Form1 : Form
    {

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息
        // 0:表示傳遞的是字符串信息
        // 1:表示傳遞的是文件信息
        // 2:表示的是震動

        /// <summary>
        /// 用來存放連接服務的IP地址和端口號,對應的Socket (這個為了以后的擴展用,現在暫時沒用)
        /// </summary>
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        /// <summary>
        /// 存儲保存文件的路徑
        /// </summary>
        string  filePath = "";
        /// <summary>
        /// 負責通信的Socket
        /// </summary>
        Socket socketSend;       

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //不檢測跨線程之間的空間調用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

        /// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }


        /// <summary>
        /// 顯示信息
        /// </summary>
        /// <param name="message"></param>
        private void ShowMsg(string message)
        {
            txtLog.AppendText(message + "\r\n");
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換為數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }  

        /// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

        /// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的坐標
            Point point = this.Location;
            //反復給窗體坐標復制一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }
    }
}

        /// 接收客戶端發來的數據,並顯示出來
        /// </summary>
        private void Recive(object o)
        {
            Socket socketSend = (Socket)o;
            try
            {
                while (true)
                {
                    //客戶端連接成功后,服務器應該接受客戶端發來的消息
                    
                    if (socketSend == null)
                    {
                        MessageBox.Show("請選擇要發送的客戶端");
                        continue;
                    }
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    //實際接受到的有效字節數
                    int r = socketSend.Receive(buffer);
                    //如果客戶端關閉,發送的數據就為空,然后就跳出循環
                    if (r == 0)
                    {
                        break;
                    }                   
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        string filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 1, r - 1);
                        }
                        ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch{}          
        }

  

(3)發送數據(這里分發送字符串、文件(包含大文件)、震動)

發送字符串信息

/// <summary>
        /// 發送信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            
            //獲得選中客戶端ip對應的通信Socket       
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            string strSend=txtMsg.Text;
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(strSend);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換為數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch
            {
            }            
        }

  發送震動

/// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {
            //判斷是否選擇了要發送的客戶端
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch
            {
                
            }
            
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的坐標
            Point point = this.Location;
            //反復給窗體坐標復制一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }

  

發送文件(包含大文件)

首先要選擇文件

/// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

  然后在發送文件

/// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //判斷是否選擇了要發送的客戶端
            if (cboUsers.SelectedItem == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            if (socketSend == null)
            {
                MessageBox.Show("請選擇要發送的客戶端");
                return;
            }
            string filePath = txtPath.Text;
            if (string.IsNullOrEmpty(filePath))
            {
                MessageBox.Show("請選擇文件");
                return;
            }
            Thread td = new Thread(SendBigFile);
            td.IsBackground = true;
            td.Start();
            
        }

        /// <summary>
        /// 大文件斷點傳送
        /// </summary>
        private void SendBigFile()
        {
            string filePath = txtPath.Text;
            Socket socketSend = dicSocket[cboUsers.SelectedItem.ToString()];
            try
            {
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    //1. 第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件
                    long length = fsRead.Length;
                    byte[] byteLength = Encoding.UTF8.GetBytes(length.ToString());
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(byteLength);
                    socketSend.Send(list.ToArray()); //
                    //2. 第二步:每次發送一個4KB的包,如果文件較大,則會拆分為多個包
                    byte[] buffer = new byte[1024 * 1024];
                    long send = 0; //發送的字節數                   
                    while (true)  //大文件斷點多次傳輸
                    {
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        if (r == 0)
                        {
                            break;
                        }
                        socketSend.Send(buffer, 0, r, SocketFlags.None);
                        send += r;
                        ShowMsg(string.Format("{0}: 已發送:{1}/{2}", socketSend.RemoteEndPoint, send, length));
                    }
                    ShowMsg("發送完成");
                    txtPath.Text = "";
                }
            }
            catch
            {

            }
        }

  

注意:(1)發送文件的時候會分兩步發送 :第一步:發送一個包,表示文件的長度,讓客戶端知道后續要接收幾個包來重新組織成一個文件

                                                 第二步:每次發送一個1MB的包,如果文件較大,則會拆分為多個包

     (2)每個客戶端連接服務端的啥時候,都會把客戶端的ip以及端口號,放到下拉框里,想給那個客戶端發信息,就選擇對應的客戶端

二、客戶端的建立

1.客戶端的項目建立以及頁面布局

客戶端的界面布局與服務端很像,就是把對應的開始監聽換成連接,當然代碼也會有所改變,后面會講到·····

2.各功能按鍵的事件代碼

先把整個服客戶端的代碼貼出來,然后我們在一一講解

namespace SocketClient
{
    public partial class Form1 : Form
    {

        //說明:在傳遞信息的時候,會在需要傳遞的信息前面加一個字符來標識傳遞的是不同的信息
        // 0:表示傳遞的是字符串信息
        // 1:表示傳遞的是文件信息
        // 2:表示的是震動

        /// <summary>
        /// 用來存放連接服務的IP地址和端口號,對應的Socket (這個為了以后的擴展用,現在暫時沒用)
        /// </summary>
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        /// <summary>
        /// 存儲保存文件的路徑
        /// </summary>
        string  filePath = "";
        /// <summary>
        /// 負責通信的Socket
        /// </summary>
        Socket socketSend;       

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //不檢測跨線程之間的空間調用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

        /// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }


        /// <summary>
        /// 顯示信息
        /// </summary>
        /// <param name="message"></param>
        private void ShowMsg(string message)
        {
            txtLog.AppendText(message + "\r\n");
        }

        /// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換為數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }  

        /// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

        /// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的坐標
            Point point = this.Location;
            //反復給窗體坐標復制一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }
    }
}

1)傳輸類型說明以及全局變量

 

這些說明以及全局變量,說的也比較清楚,也不累贅了。

2)Socket通信服務端具體步驟:

(這些步驟都是根據第一個圖來的)

 (1)建立一個通信的Socket

/// <summary>
        /// 建立連接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                //創建負責通信的Socket
                socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //獲取服務端的IP
                IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
                //獲取服務端的端口號
                IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
                //獲得要連接的遠程服務器應用程序的IP地址和端口號
                socketSend.Connect(port);
                ShowMsg("連接成功");
                //新建線程,去接收客戶端發來的信息
                Thread td = new Thread(AcceptMgs);
                td.IsBackground = true;
                td.Start();
            }
            catch { }
        }

在連接按鈕里,我們建立了Socket

由於客戶端會不停的去監視接收服務端發來的信息,如果把這個工作放到主線程里,程序會出現假死的現象,所以這里給他放到一個新的線程里。

(2)接收信息

/// <summary>
        /// 接收數據
        /// </summary>
        private void AcceptMgs()
        {           
            try
            {
                /// <summary>
                /// 存儲大文件的大小
                /// </summary>
                long length = 0;
                long recive = 0; //接收的大文件總的字節數
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int r = socketSend.Receive(buffer);
                    if (r == 0)
                    {
                        break;
                    }
                    if (length > 0)  //判斷大文件是否已經保存完
                    {
                        //保存接收的文件
                        using (FileStream fsWrite = new FileStream(filePath, FileMode.Append, FileAccess.Write))
                        {
                            fsWrite.Write(buffer, 0, r);
                            length -= r; //減去每次保存的字節數
                            ShowMsg(string.Format("{0}: 已接收:{1}/{2}", socketSend.RemoteEndPoint, recive-length, recive));
                            if (length <= 0)
                            {
                                ShowMsg(socketSend.RemoteEndPoint + ": 接收文件成功");
                            }
                            continue;
                        }                        
                    }
                    if (buffer[0] == 0) //如果接收的字節數組的第一個字節是0,說明接收的字符串信息
                    {
                        string strMsg = Encoding.UTF8.GetString(buffer, 1, r - 1);
                        ShowMsg(socketSend.RemoteEndPoint.ToString() + ": " + strMsg);
                    }
                    else if (buffer[0] == 1) //如果接收的字節數組的第一個字節是1,說明接收的是文件
                    {
                        length = int.Parse(Encoding.UTF8.GetString(buffer,1,r-1));
                        recive = length;
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
                        //如果沒有選擇保存文件路徑就一直打開保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }                                        
                    }
                    else if (buffer[0] == 2) //如果接收的字節數組的第一個字節是2,說明接收的是震動
                    {
                        ZD();
                    }
                }
            }
            catch { }
            
           
        }

接收信息是會根據接收到字節數字的第一個字節來判斷接收到的是什么,如果接收的是個大文件,首先會接收大文件的大小,然后根據大小接收相同大小的字節數組追加保存到一個文件里去。

(3)發送數據(這里分發送字符串、文件(包含大文件)、震動)

發送字符串信息

/// <summary>
        /// 發送數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
                //獲得發送的信息時候,在數組前面加上一個字節 0
                List<byte> list = new List<byte>();
                list.Add(0);
                list.AddRange(buffer);
                //將泛型集合轉換為數組
                byte[] newBuffer = list.ToArray();
                //將了標識字符的字節數組傳遞給客戶端
                socketSend.Send(newBuffer);
                txtMsg.Text = "";
            }
            catch{}          
        }

發送震動

/// <summary>
        /// 震動
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnZD_Click(object sender, EventArgs e)
        {        
            try
            {
                // 首字節是2說明是震動
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                socketSend.Send(buffer);
            }
            catch{ }
        }

        /// <summary>
        /// 震動
        /// </summary>
        private void ZD()
        {
            //獲取當前窗體的坐標
            Point point = this.Location;
            //反復給窗體坐標復制一百次,達到震動的效果
            for (int i = 0; i < 100; i++)
            {
                this.Location = new Point(point.X - 5, point.Y - 5);
                this.Location = new Point(point.X + 5, point.Y + 5);
            }
            this.Location = point;
        }

發送文件(不包含大文件)

首先要選擇文件

/// <summary>
        /// 選擇文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            //打開文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "選擇要傳的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|圖片文件|*.jpg|視頻文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到選擇文件的路徑
            txtPath.Text = ofd.FileName;
        }

然后在發送文件

/// <summary>
        /// 發送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {       
            try
            {
                string filePath = txtPath.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("請選擇文件");
                    return;
                }
                //讀取選擇的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //獲得發送的信息時候,在數組前面加上一個字節 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //將了標識字符的字節數組傳遞給客戶端
                    socketSend.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    txtPath.Text = "";
                }
            }
            catch{ }
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM