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>
        /// 接收客户端发来的数据,并显示出来
        /// </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{ }
        }

到此,客户端以及服务端都写好了,就可以启动客户端和服务端进行通信了,由于我是在一个电脑上模拟,就用的是相同的ip,如:

至此,整个Socket的通信就结束了,如有什么建议希望不吝赐教·····大家一起学习······

时间: 2024-11-29 03:37:55

C# Socket服务端与客户端通信(包含大文件的断点传输)的相关文章

socket服务端和客户端

#!/usr/bin/env python#encoding: utf-8import socketdef handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, World") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREA

Java网络编程【Socket服务端和客户端】

Socket 编程大家都不陌生,Java 学习中必学的部分,也是 Java网络编程核心内容之一.Java 网络编程又包括 TCP.UDP,URL 等模块.TCP 对应 Socket模块,UDP 对应 DatagramPacket 模块.URL 对应 URL 模块.其中 TCP 和 UDP 是网络传输协议,TCP 是数据流传输协议,UDP 是数据包传输协议.两者之间的异同就不在这里说了,推荐一本入门书籍 <TCPIP入门经典>.我们开始 Socket 服务端和客户端编程吧. 一.Socket 服

C# Socket服务端和客户端互相send和receive

服务端 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Net.Sockets; 5 using System.Net; 6 using System.Threading; 7  8 namespace Controller 9 {10     public static class SocketServer11     {12         private stat

Socket——服务端与客户端交互的简单实现

服务端: package socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socke

socket系列之socket服务端与客户端如何通信

上面已经分别介绍了ServerSocket跟Socket的工作步骤,并且从应用层往系统底层剖析其运作原理,我们清楚了他们各自的一块,现在我们将把他们结合起来,看看他们是如何通信的,并详细讨论一下他们之间相互通信的一些细节. 借助图2-3-2-4,想象一下你正在大学课室上着电脑,你跟你另外两个朋友觉得老师讲得课很菜,没必要听,于是你们仨都各自打开浏览器冲浪,刚好你们访问了同一台服务器,假如你用的是浏览器A,那么整个流程为: ① 浏览器确认目标IP跟目标端口号(http默认使用80端口),当然如果你

python模拟服务端和客户端通信(简单)

写了一个超级简单的,小白可以看一下,因为自己就是小白白(自学的) 客户端: import socket#指定一个协议tcp/ipclient=socket.socket()#连接一个地址(服务端和端口号)client.connect(('localhost',6969))#发送数据(python默认是byte类型,所以数据前面加b)client.send(b"Hello Word")#把服务端反馈的信息进行接收并赋值,给出一个接收的大小(kb/M)之类的date=client.recv

golang的socket服务端与客户端

服务端 服务端的处理流程 监听端口 接收客户端的链接 创建goroutine,处理该链接 package main import ( "fmt" "net" ) func main() { fmt.Println("start server...") listen, err := net.Listen("tcp", "0.0.0.0:50000") if err != nil { fmt.Println(&

Socket服务端和客户端(C++,CodeBlocks+GCC编译)

//main.cpp 1 #include "j_socket.h" 2 #include <stdio.h> 3 #include <pthread.h> 4 static int port=21; 5 j_server* ser; 6 void* main_listen( void* args) 7 { 8 ser=new j_server(port); 9 ser->j_listen(); 10 } 11 int main() 12 { 13 sta

Android—Socket服务端与客户端用字符串的方式互相传递图片

发送图片: 首先找到具体传递的图片: private Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); // 开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcP