• C# Socke t网络编程


    效果展示

      ①客户端发送消息给服务器

        

      ②服务器发送消息给指定客户端

        

      ③服务器群发消息给客户端

         

       ③服务器发送文件给客户端

       ④服务器给客户端发送震动指令

    1、什么是Socket网络编程

      两台计算机相互通信靠的就是Socket,类似两个人要通信靠电话,也就是说Socket就是电脑间(程序间)的电话机
      Socket英文原意是孔、插座,作为进程通信机制,取后一种意思,通常也称套接字,用于描述IP地址和端口。IP地址指向某平台服务器,端口用于连接到某一个应用程序。

        

      Socket在通信过程中所处位置(作用)理解:

        

        释义:男生要到女生宿舍找女朋友出去玩,不能直接进入女生宿舍去找,要经过宿管大妈,由宿管大妈打电话告知你的女朋友,电话打通后你们之间就能通话了。

        这里的宿管大妈就是负责监听的Socket,如果有男生(客户端发送请求)来了就创建负责通信的Socket(电话机),从而使该男生(客户端)与对应女生(服务器

        某个应用程序)可以进行通信了。

     2、Socket支持的协议(TCP协议、UDP协议)

      协议:类似与两个人打电话有一个默认协议就是都说普通话,如果张三是四川人李四是上海人他们都说家乡话(当地方言),可能他们都听不懂对方的话,在网络中常用的协议有:TCP协议、UDP协议

      TCP/IP协议(Transmission Control Protocol/Internet Protocol):传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网设计的。

      TCP协议:属于TCP/IP协议中的一种,相对于UDP协议安全稳定,在TCP传输过程中要经过3次握手,传输效率相对低。

        

         释义:客户端像服务器发送消息(你有空吗?),服务器回复(我有空),客户端再发给服务器(我晓得你有空了),经过了3次握手,客户端和服务器才开始通信。

      UDP协议(User Data Protocol):用户数据协议,与TCPx相对应的协议。他也属于TCP/IP协议的一种,它只管发,不管对方有没有接收,相对于TCP协议快速、效率

      高、不稳定,容易发生数据丢失。客户端直接发给服务器发送消息,不管服务器是否有空接收数据等,都发给服务器。类似于电报、电台广播。

      协议的选择?

        两种传输协议比较,无论好坏各有各自的优点。

        当数据传输的性能必须让位于数据传输的完整性、可控性和可靠性时,TCP协议(安全稳定)是当然的选择。

        当强调传输性能而不是传输完整性时,如:音频和多媒体应用,UDP协议是最好的选择。也就是说视频传输的时候用UDP协议(快速效率高),因为视频聊天时更希

        望看到对方流畅不卡,但是清晰度可能低一点。

      2、端口分类

      公认端口:也称为常用端口,端口号为0~1023,它们紧密的绑定一些特殊服务。通常这些端口的通信明确了某种服务协议,不可再重新定义它的作用对象。如80端口

      (http通信)、23端口(Telnet服务)。

      注册端口端口号为1024~49151,它们松散的绑定一些服务,也有许多服务绑定了这些端口,这些端口同样也用于其他目的,且多数没有明确定义对象,不同程序可以

      根据需求自己定义,常用于大企业。这些端口对网络的安全十分重要,所以对于服务器一般来说要关闭这些端口。

      动态端口/私有端口端口号为49152~65535,理论上不应该把常用服务分配在这些端口上,但实际上有较为特殊的程序,特别是木马就非常喜欢这些端口,因为这些端口

      常常不会引起人们注意,容易隐藏。

      3、Socket通信的基本流程及如何创建Socke

      ①、Socket通信的基本流程

        

       ②创建服务器界面

        创建一个解决方案名称Socket网络编程,在其中创建Windows 窗体应用程序(.NET Framework)名称SocketChatServer,窗体改名为FrmSocketServer。

        Ip:本地Ip地址

        Port:端口号(动态端口/私有端口

        UserIp:客户端的Ip地址

        

         服务器代码

    public partial class FrmSocketServer : Form
        {
            public FrmSocketServer()
            {
                InitializeComponent();
    
                // 取消捕获对错误线程的调用(解决跨域线程的问题)
                //Control.CheckForIllegalCrossThreadCalls = false;
            }
    
            //定义一个键值对集合用于存储客户端的Ip、端口及负责通信的Socket
            Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
    
            List<Socket> clientCommunicationSocketList = new List<Socket>();
    
            private void StartMonitor_Click(object sender, EventArgs e)
            {
                // 创建负责监听的Socket
                // 第一个参数AddressFamily设置网络寻址协议,InterNetwork表示IPV4
                // 第二个参数SocketType设置数据传输方式(Socket类型),这个要根据第三个参数来设置,Stream此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接
                // 第三个参数为UDP协议时,第二个参数就要为Dgram(报文)
                Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                // 绑定监听的Ip和Port
                IPAddress ip = IPAddress.Parse(textIp.Text);
                int port = Convert.ToInt32(textPort.Text);
                IPEndPoint endPoint = new IPEndPoint(ip, port);
                socketWatch.Bind(endPoint);
    
                ShowMsg("监听成功");
                StartMonitor.Enabled = false;
    
                // 设置监听队列数量(如果监听太多就要使用分流)
                socketWatch.Listen(10);
    
                // 创建一个线程去执行这个方法
                Thread thread = new Thread(Listen);
    
                // 设置后台线程
                thread.IsBackground = true;
    
                // 标记这个线程准备就绪了,可以随时被执行,具体什么时候执行这个线程由CPU决定
                thread.Start(socketWatch);
            }
    
            Socket socketCommunication;
    
            /// <summary>
            /// 让线程去执行负责通信的Socket
            /// </summary>
            /// <param name="obj"></param>
            private void Listen(object obj)
            {
                Socket socketWatch = obj as Socket;
    
                // 不断地监听客户端有没有发送请求过来
                while (true)
                {
                    // 创建负责监听的Socket
                    socketCommunication = socketWatch.Accept();
                    dicSocket.Add(socketCommunication.RemoteEndPoint.ToString(), socketCommunication);
                    clientCommunicationSocketList.Add(socketCommunication);
                    ShowMsg(socketCommunication.RemoteEndPoint.ToString() + "连接成功");
    
                    // 解决跨线程的访问
                    if (cboUser.InvokeRequired)
                    {
                        cboUser.Invoke(new Action(() =>
                        {
                            cboUser.Items.Add(socketCommunication.RemoteEndPoint.ToString());
                        }), null);
                    }
                    else
                    {
                        cboUser.Items.Add(socketCommunication.RemoteEndPoint.ToString());
                    }
    
                    // 创建一个线程去执行接收客户端发送过来消息这个方法
                    Thread thread = new Thread(Receive);
    
                    // 设置后台线程
                    thread.IsBackground = true;
    
                    // 标记这个线程准备就绪了,可以随时被执行,具体什么时候执行这个线程由CPU决定
                    thread.Start(socketCommunication);
                }
            }
    
            /// <summary>
            /// 接收客户端发送过来消息
            /// </summary>
            /// <param name="obj"></param>
            private void Receive(object obj)
            {
                Socket socketWatch = obj as Socket;
    
                // 定义接收数据的大小
                byte[] buffer = new byte[1024 * 1024 * 2];
    
                while (true)
                {
                    try
                    {
                        // r 表示实际接收数据的字节数
                        int r = socketWatch.Receive(buffer);
    
                        // 解决(关闭窗口后 r=0)
                        if (r == 0)
                        {
                            // break;
                            socketWatch.Shutdown(SocketShutdown.Both);
                            socketWatch.Close();
                            return;
                        }
                        // 字节数据转字符串
                        string str = Encoding.UTF8.GetString(buffer, 0, r);
                        // 显示消息格式:客户端Ip端口,消息
                        ShowMsg(socketWatch.RemoteEndPoint.ToString() + ":" + str);
                    }
                    catch
                    {
                    }
                }
            }
    
            private void ShowMsg(string str)
            {
                // 解决跨线程的访问
                if (textReceiveMsg.InvokeRequired)
                {
                    textReceiveMsg.Invoke(new Action<string>(s =>
                    {
                        textReceiveMsg.AppendText(s + "
    ");
                    }), str);
                }
                else
                {
                    //textReceiveMsg.Text = str + "
    " + textReceiveMsg.Text;
                    textReceiveMsg.AppendText(str + "
    ");
                }
            }
    
            /// <summary>
            /// 发送消息
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SendMsg_Click(object sender, EventArgs e)
            {
                string str = textSendMsg.Text.Trim();
    
                // 字符串转字节数组
                byte[] buffer = Encoding.UTF8.GetBytes(str);
    
                #region 第一种思路
                byte[] newBuffer = new byte[buffer.Length + 1];
                newBuffer[0] = 0;
                Buffer.BlockCopy(buffer, 0, newBuffer, 1, buffer.Length);
                #endregion
    
                #region 第二种思路
                //List<byte> list = new List<byte>();
                //list.Add(0);
                //list.AddRange(buffer);
                //byte[] newBuffer = list.ToArray();
                #endregion
    
                string ipPort = cboUser.SelectedItem.ToString();
    
                // 只能给最后一个客户端发送消息
                //socketCommunication.Send(buffer);
    
                // 给指定客户端发送消息
                dicSocket[ipPort].Send(newBuffer);
    
                textSendMsg.Clear();
            }
    
            /// <summary>
            /// 群发消息
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void BulkMsg_Click(object sender, EventArgs e)
            {
                foreach (var socket in clientCommunicationSocketList)
                {
                    if (socket.Connected)
                    {
                        string str = textSendMsg.Text.Trim();
    
                        // 字符串转字节数组
                        byte[] buffer = Encoding.UTF8.GetBytes(str);
                        
                        
                        #region 第一种思路
                        //byte[] newBuffer = new byte[buffer.Length + 1];
                        //newBuffer[0] = 0;
                        //Buffer.BlockCopy(buffer, 0, newBuffer, 1, buffer.Length);
                        #endregion
    
                        #region 第二种思路
                        List<byte> list = new List<byte>();
                        list.Add(0);
                        list.AddRange(buffer);
                        byte[] newBuffer = list.ToArray();
                        #endregion
    
                        socket.Send(newBuffer);
                    }
                }
                textSendMsg.Clear();
            }
    
            /// <summary>
            /// 选择文件
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SelectFile_Click(object sender, EventArgs e)
            {
                OpenFileDialog ofd = new OpenFileDialog();
    
                // 初始目录
                ofd.InitialDirectory = @"C:UsersDHRDesktop";
                ofd.Title = "请选择要发送的文件";
                ofd.Filter = "所有文件|*.*|txt文本文档|*.txt";
                ofd.ShowDialog();
    
                textPath.Text = ofd.FileName;
            }
    
            /// <summary>
            /// 发送文件
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SendFile_Click(object sender, EventArgs e)
            {
                #region 第一种思路,使用文件流方式读取
                string path = textPath.Text;
                using (FileStream fsRead = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    // 定义发送文件的大小
                    byte[] buffer = new byte[1024 * 1024 * 3];
                    // 实际读取到的字节数(文件存储到buffer数组中)
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
    
                    string ipPort = cboUser.SelectedItem.ToString();
    
                    // 为什么要加后面3个参数(newBuffer这里是3M+1byte 实际文件可能不会超过3M)
                    dicSocket[ipPort].Send(newBuffer, 0, r + 1, SocketFlags.None);
                }
                #endregion
    
                #region 第二种思路,使用File.ReadAllBytes读取
                //using (OpenFileDialog ofd = new OpenFileDialog())
                //{
                //    if (ofd.ShowDialog() != DialogResult.OK)
                //    {
                //        return;
                //    }
                //    // 把文件写入到字节数组
                //    byte[] buffer = File.ReadAllBytes(ofd.FileName);
                //    byte[] newBuffer = new byte[buffer.Length + 1];
                //    newBuffer[0] = 1;
                //    Buffer.BlockCopy(buffer, 0, newBuffer, 1, buffer.Length);
    
                //    string ipPort = cboUser.SelectedItem.ToString();
    
                //    dicSocket[ipPort].Send(newBuffer, SocketFlags.None);
                //}
                #endregion
            }
    
            private void Shock_Click(object sender, EventArgs e)
            {
                byte[] buffer = new byte[1];
                buffer[0] = 2;
                string ipPort = cboUser.SelectedItem.ToString();
    
                dicSocket[ipPort].Send(buffer);
            }
        }

      ③创建客户端界面

        

         客户端代码

        public partial class FrmSocketClinet : Form
        {
            public FrmSocketClinet()
            {
                InitializeComponent();
    
                // 取消捕获对错误线程的调用(解决跨域线程的问题)
                //Control.CheckForIllegalCrossThreadCalls = false;
            }
    
            Socket socketCommunication;
    
            private void StartConnect_Click(object sender, EventArgs e)
            {
                // 创建负责通信的Socket
                // 第一个参数AddressFamily设置网络寻址协议,InterNetwork表示IPV4
                // 第二个参数SocketType设置数据传输方式(Socket类型),这个要根据第三个参数来设置,Stream此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接
                // 第三个参数为UDP协议时,第二个参数就要为Dgram(报文)
                socketCommunication = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                // 连接到服务器
                IPAddress ip = IPAddress.Parse(textIp.Text);
                int port = Convert.ToInt32(textPort.Text);
                IPEndPoint endPoint = new IPEndPoint(ip, port);
                socketCommunication.Connect(endPoint);
    
                ShowMsg("连接成功");
                StartConnect.Enabled = false;
    
                // 创建一个线程去执行接收服务器发送过来消息这个方法
                Thread thread = new Thread(Receive);
    
                // 设置后台线程
                thread.IsBackground = true;
    
                // 标记这个线程准备就绪了,可以随时被执行,具体什么时候执行这个线程由CPU决定
                thread.Start();
            }
    
            /// <summary>
            /// 接收服务器发送过来消息
            /// </summary>
            /// <param name="obj"></param>
            private void Receive()
            {
                // 定义接收数据的大小
                byte[] buffer = new byte[1024 * 1024 * 2];
    
                while (true)
                {
                    try
                    {
                        // r 表示实际接收数据的字节数
                        int r = socketCommunication.Receive(buffer);
    
                        // 解决(服务器关闭 r=0)
                        if (r == 0)
                        {
                            // break;
                            socketCommunication.Shutdown(SocketShutdown.Both);
                            socketCommunication.Close();
                            return;
                        }
                        int n = buffer[0];
    
                        // 文本d
                        if (n == 0)
                        {
                            // 字节数据转字符串
                            //string str = Encoding.UTF8.GetString(buffer, 0, r);
                            string str = Encoding.UTF8.GetString(buffer, 1, r - 1);
    
                            // 显示消息格式:客户端Ip端口,消息
                            ShowMsg(socketCommunication.RemoteEndPoint.ToString() + ":" + str);
                        }
                        // 文件
                        else if (n == 1)
                        {
                            #region 第一种思路,使用文件流写
                            SaveFileDialog sfd = new SaveFileDialog();
                            sfd.InitialDirectory = @"C:UsersDHRDesktop";
                            sfd.Title = "请选择要保存文件的位置";
                            sfd.Filter = "所有文件|*.*|txt文本文档|*.txt";
    
                            // 解决跨线程的访问
                            if (this.InvokeRequired)
                            {
                                this.Invoke(new Action(() =>
                                {
                                    sfd.ShowDialog(this);
                                }), null);
                            }
                            else
                            {
                                sfd.ShowDialog(this);
                            }
    
                            string path = sfd.FileName;
                            using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
                            {
                                fsWrite.Write(buffer, 1, r - 1);
                            }
                            MessageBox.Show("保存成功");
                            #endregion
    
                            #region 第二种思路 使用File.ReadAllBytes写
                            //using (SaveFileDialog sfd = new SaveFileDialog())
                            //{
                            //    sfd.InitialDirectory = @"C:UsersDHRDesktop";
                            //    sfd.Title = "请选择要保存文件的位置";
                            //    sfd.Filter = "所有文件|*.*|txt文本文档|*.txt";
                            //    sfd.DefaultExt = "txt";
    
                            //    // 解决跨线程的访问
                            //    if (this.InvokeRequired)
                            //    {
                            //        this.Invoke(new Action(() =>
                            //        {
                            //            if (sfd.ShowDialog(this) != DialogResult.OK)
                            //            {
                            //                return;
                            //            }
                            //        }), null);
                            //    }
                            //    else
                            //    {
                            //        if (sfd.ShowDialog(this) != DialogResult.OK)
                            //        {
                            //            return;
                            //        }
                            //    }
    
                            //    byte[] newBuffer = new byte[r - 1];
                            //    Buffer.BlockCopy(buffer, 1, newBuffer, 0, r - 1);
                            //    File.WriteAllBytes(sfd.FileName, newBuffer);
                            //    MessageBox.Show("保存成功");
                            //}
                            #endregion
    
                        }
                        // 震动
                        else if (n == 2)
                        {
                            Shock();
                        }
                    }
                    catch (Exception e)
                    {
    
                    }
                }
            }
    
            /// <summary>
            ///震动方法
            /// </summary>
            private void Shock()
            {
                // 记录窗体的初始位置
                Point p = new Point(this.Location.X, this.Location.Y);
               
                // 初始化Random对象
                Random r = new Random();
    
                // 解决跨线程的访问
                if (this.InvokeRequired)
                {
                    this.Invoke(new Action(() =>
                    {
                        for (int i = 0; i < 3000; i++)
                        {
                            this.Location = new Point(p.X + r.Next(-2, 2), p.Y + r.Next(-2, 2));
    
                        }
                    }), null);
                }
                else
                {
                    for (int i = 0; i < 3000; i++)
                    {
                        this.Location = new Point(p.X + r.Next(-10, 10), p.Y + r.Next(-10, 10));
                    }
                }
    
            }
    
            private void ShowMsg(string str)
            {
                // 解决跨线程的访问
                if (textReceiveMsg.InvokeRequired)
                {
                    textReceiveMsg.Invoke(new Action<string>(s =>
                    {
                        textReceiveMsg.AppendText(s + "
    ");
                    }), str);
                }
                else
                {
                    //textReceiveMsg.Text = str + "
    " + textReceiveMsg.Text;
                    textReceiveMsg.AppendText(str + "
    ");
                }
            }
    
    
            /// <summary>
            /// 发送消息
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SendMsg_Click(object sender, EventArgs e)
            {
                string str = textSendMsg.Text.Trim();
    
                // 字符串转字节数组
                byte[] buffer = Encoding.UTF8.GetBytes(str);
                socketCommunication.Send(buffer);
    
                textSendMsg.Clear();
            }
        }

      需要代码的关注私聊哦!

      后续会陆续更新其他资料,喜欢请关注哦!

      我的博客:https://www.cnblogs.com/duhaoran/

  • 相关阅读:
    使用wchar_t输入,显示中文
    MFC使用rich edit若干问题
    一种对话框嵌入MFC 文档结构效果的实现方法(一),让你的自定义对话框区域同客户区大小一起改变
    一种在MFC程序上显示jpeg图片的方法(二)曙光乍现
    一种在MFC程序上显示jpeg图片的方法(一)宁滥勿缺
    MFC---GDI之DC类杂记,以画尺子为例
    又让厂公着半天急----一例自定义MFC程序编译时LNK2019错误
    egret 引擎分析之三————egret make --egretversion xxxx 到底做了什么?
    egret 引擎分析之二————从selector.js说起
    egret 引擎分析之一————egret 命令的时候发生了什么
  • 原文地址:https://www.cnblogs.com/duhaoran/p/12924344.html
Copyright © 2020-2023  润新知