2014-9-4 Socket

2014-9-4 复习Socket

1、套接字(也就是Socket)

用于描述IP地址和端口,是一个通信链的句柄。

其实就是两个程序通信用的)

2、有两种类型

流式Socket(Stream):

是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低。

数据报式Socket(DataGram):

是一种无连接的Socket,对应于无连接的UDP服务应用,不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高。

3、Socket一般应用模式(服务器端和客户端)



4、聊天程序

1.服务端

namespace 聊天程序_服务端
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        //定义一个更新UI的委托
        private delegate void DGUpdateUI(string msg);
        //实例一个更新UI的委托
        private DGUpdateUI DgUpdateUI;
        private DGUpdateUI DgShowMsg;
        
        //定义一个字典用来储存用户连接时创建的连接套接字
        private Dictionary<string, Socket> dictClient = new Dictionary<string, Socket>();
        //定义一个字典用来储存用户连接时创建监听线程
        private Dictionary<string, Thread> dictClientThread = new Dictionary<string, Thread>();
        /// <summary>
        /// 开启服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartServer_Click(object sender, RoutedEventArgs e)
        {
            //把更新UI的方法赋值给更新UI委托
            DgUpdateUI = UpadteUI;
            DgShowMsg = ShowMsg;
            //创建一个监听套接字
            Socket socketWatch = SocketHelper.CreateWatchSocket(txtIP.Text.Trim(), int.Parse(txtPort.Text.Trim()),10);
            //创建一个监听线程
            Thread threadWatch = new Thread(delegate()
            {
                Watch(socketWatch);
            });
            //设置为后台线程
            threadWatch.IsBackground = true;
            //开启监听线程
            threadWatch.Start();
            
            //开启成功
            ShowMsg("开启服务成功!");
            txtIP.IsEnabled = false;
            txtPort.IsEnabled = false;
            btnStartServer.IsEnabled = false;
        }


      /// <summary>
      /// 发送消息
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, RoutedEventArgs e)
      {
           
          //拿到key值
          string key = lbOnlineClient.SelectedValue.ToString();
          //拿到发送的信息
          string sendMsg = txtSendMsg.Text.Trim();
          //转成字节数组 
          byte[] arrMsg = Encoding.UTF8.GetBytes(sendMsg);
          byte[] arrSendMsg = new byte[arrMsg.Length + 1];
          arrSendMsg[0] = 0;
          Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
         
            //群发
          if (cbGroupSend.IsChecked == true)
          {
              foreach (Socket s in dictClient.Values)
              {
                  s.Send(arrSendMsg);
              }
              ShowMsg("你 对 所有人 说:" + sendMsg);
          }
              //单个发
          else
          {
              dictClient[key].Send(arrSendMsg);
              ShowMsg("你 对 " + key + " 说:" + sendMsg);
          }        
         
      }


        /// <summary>
        /// 选择文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnChooseFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                txtFile.Text = ofd.FileName;
            }
        }
        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, RoutedEventArgs e)
        {
            //拿到key值
            string key = lbOnlineClient.SelectedValue.ToString();
            //拿到发送的文悠扬
            string sendFilePath = txtFile.Text;
            using (FileStream fs = new FileStream(sendFilePath, FileMode.Open))
            {
                //创建一个缓冲区
                byte[] arrMsg = new byte[1024*1024*2];
                //把流写入缓冲区
                int arrLen=fs.Read(arrMsg, 0, arrMsg.Length);
                //再创建一个发送文件(标识)缓冲区
                byte[] arrSendMsg = new byte[arrLen + 1];
                arrSendMsg[0] = 1;//代表发送的是文件
                Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrLen);
                //群发
                if (cbGroupSend.IsChecked == true)
                {
                    foreach (Socket s in dictClient.Values)
                    {
                        s.Send(arrSendMsg);
                    }
                    ShowMsg("你 对 所有人 发送了:" + sendFilePath);
                }
                //单个发
                else
                {
                    dictClient[key].Send(arrSendMsg);
                    ShowMsg("你 对 " + key + " 发送了:" + sendFilePath);
                }        
            }
        }


        //方法
        //1.监听方法
        void Watch(Socket socketWatch)
        {
            while (true)
            {
                Socket socketClient = socketWatch.Accept();
                //把用户连接时创建的socket放到dictClient中
                dictClient.Add(socketClient.RemoteEndPoint.ToString(), socketClient);
                
                //为每个用户新建一个接收线程,用来监听用户发来的消息
                Thread threadClient = new Thread(delegate()
                {
                    Receive(socketClient);
                });
                //把通信线程以socketClient.RemoteEndPoint 为key,保存起来
                dictClientThread.Add(socketClient.RemoteEndPoint.ToString(), threadClient);
                threadClient.IsBackground = true;
                threadClient.Start();
                this.Dispatcher.Invoke(DgUpdateUI, socketClient.RemoteEndPoint.ToString()); 
            }  
        }


        //2.更新UI方法
        void UpadteUI(string msg)
        {
            ShowMsg(msg +"连接成功!");
            lbOnlineClient.Items.Add(msg);
        }


        //3.显示消息
        void ShowMsg(string msg)
        {
            txtMsg.AppendText(msg + "\r\n");
        }
        //4.接收消息
        void Receive(Socket socketClient)
        {
            while (true)
            {
                //创建一个2M的字节数组,用来存放接收到数据
                byte[] arrRecMsg = new byte[1024*1024*2];
                //开启接收 并把实际接收到的数据长度存在arrLen中
                int arrLen = -1;


                try
                {
                    arrLen = socketClient.Receive(arrRecMsg);
                }
                catch (SocketException ex)
                {
                    this.Dispatcher.Invoke(DgShowMsg, "异常:" + ex.Message);
                    //删了断开的通信套接字
                    dictClient.Remove(socketClient.RemoteEndPoint.ToString());
                    dictClientThread.Remove(socketClient.RemoteEndPoint.ToString());
                    this.Dispatcher.Invoke(delegate()
                    {
                        RemoveListBox(socketClient.RemoteEndPoint.ToString());
                    });
                    break;
                }
                catch (Exception ex)
                {
                    this.Dispatcher.Invoke(DgShowMsg, "异常:" + ex.Message);
                    break;
                }
                //0 代表发的是消息 1 代表是文件
                if (arrRecMsg[0] == 0)
                {
                    //把字节数组转成字符串 byte[]--string
                    string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 1, arrLen-1);
                    //显示消息
                    this.Dispatcher.Invoke(DgShowMsg, socketClient.RemoteEndPoint.ToString() + " 对 你 说:" + strRecMsg);
                }
                   // 1 代表是文件
                else if (arrRecMsg[0] == 1)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    if (sfd.ShowDialog() == true)
                    {
                        string saveFilePath = sfd.FileName;
                        using (FileStream fs = new FileStream(saveFilePath, FileMode.Create))
                        {
                            fs.Write(arrRecMsg, 1, arrLen - 1);
                            if (fs.Length == arrLen - 1)
                            {
                                this.Dispatcher.Invoke(DgShowMsg, "文件保存成功!" + saveFilePath);
                            }
                            else
                            {
                                this.Dispatcher.Invoke(DgShowMsg, "文件保存失败!");
                            }
                        }
                    }
                }
                
            }
            
        }
        //5.删除ListBox
        void RemoveListBox(string key)
        {
            lbOnlineClient.Items.Remove(key);
        }
     


    }


    class SocketHelper
    {
        /// <summary>
        /// 创建一个连接套接字
        /// </summary>
        /// <param name="ip">IP地址</param>
        /// <param name="port">端口</param>
        /// <param name="listenLen">监听队列长度</param>
        /// <returns></returns>
        public static Socket CreateWatchSocket(string ip, int port,int listenLen)
        {
            //创建一个监听套接字
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //创建一个IP地址
            IPAddress address = IPAddress.Parse(ip);
            //创建一个包含IP和端口的网络节点
            IPEndPoint endPoint = new IPEndPoint(address, port);
            //绑定网络节点
            socketWatch.Bind(endPoint);
            //设置监听队列长度
            socketWatch.Listen(listenLen);
            //返回监听套接字
            return socketWatch;


        }
    }
}
2.客户端

namespace Client
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        //定义一个更新UI的委托
        private delegate void DGUpdateUI(string msg);
        //实例一个更新UI的委托
        private DGUpdateUI DgUpdateUI;
        //连接套接字
        private Socket socketConn=null;


        /// <summary>
        /// 连接服务
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartServer_Click(object sender, RoutedEventArgs e)
        {
            DgUpdateUI = UpadteUI;
            //创建一个连接套接字
            socketConn=SocketHelper.CreateConnSocket(txtIP.Text.Trim(), int.Parse(txtPort.Text.Trim()));
            //创建一个监听Receive线程
            Thread threadReceive = new Thread(Receive);
            //设置为后台
            threadReceive.IsBackground = true;
            //开启线程
            threadReceive.Start();
        }


        /// <summary>
        /// 发送信息给服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, RoutedEventArgs e)
        {
            string strSendMsg = txtSendMsg.Text.Trim();
            byte[] arrMsg = Encoding.UTF8.GetBytes(strSendMsg);
            byte[] arrSendMsg = new byte[arrMsg.Length + 1];
            arrSendMsg[0] = 0;
            Buffer.BlockCopy(arrMsg, 0, arrSendMsg, 1, arrMsg.Length);
            socketConn.Send(arrSendMsg);
            ShowMsg("你对 服务器 说" + strSendMsg);
        }


        /// <summary>
        /// 选择文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnChooseFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                txtFilePath.Text = ofd.FileName;
            }
        }


        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, RoutedEventArgs e)
        {
            //拿到要保存的文件路径
            string filePath = txtFilePath.Text;
            //创建文件流
            using (FileStream fs = new FileStream(filePath,FileMode.Open))
            {
                //创建一个字节数组 2M
                byte[] arrFile = new byte[1024*1024*2];
                //从流中写入byte的缓冲区中 返回实际写入的长度
                int arrLen=fs.Read(arrFile, 0, arrFile.Length);
                //创建一个标识数组
                byte[] arrSendFile = new byte[arrLen + 1];
                arrSendFile[0] = 1;//1代表发的是文件 0代表发的是信息
                //复制数据
                Buffer.BlockCopy(arrFile, 0, arrSendFile, 1, arrLen);
                int arrSendLen=socketConn.Send(arrSendFile);
                if (arrSendLen == arrLen+1)
                {
                    ShowMsg("发送文件成功");
                }
                else
                {
                    ShowMsg("发送文件出错!");
                }
                
            }
        }
        //方法
        //1.接收消息(文件)方法
        void Receive()
        {
            while (true)
            {
                //创建一个2M的字节数组,用来存放接收到数据
                byte[] arrRecMsg = new byte[1024 * 1024 * 2];
                //开启接收 并把实际接收到的数据长度存在arrLen中
                int arrLen = socketConn.Receive(arrRecMsg);
                //0 是消息 1是文件
                if (arrRecMsg[0] == 0)
                {
                    //把字节数组转成字符串 byte[]--string
                    string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 1, arrLen-1);
                    //显示消息
                    this.Dispatcher.Invoke(DgUpdateUI, strRecMsg);
                }
                else if (arrRecMsg[0] == 1)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    if (sfd.ShowDialog() == true)
                    {
                        string saveFileName = sfd.FileName;
                        using (FileStream fs = new FileStream(saveFileName, FileMode.Create))
                        {
                            fs.Write(arrRecMsg, 1, arrLen - 1);
                            if (fs.Length == arrLen - 1)
                            {
                                //显示消息
                                this.Dispatcher.Invoke(DgUpdateUI, "文件保存成功!");
                            }
                            else
                            {
                                this.Dispatcher.Invoke(DgUpdateUI, "文件保存失败!");
                            }
                           
                           
                        }       
                    }
                   
                }
                
            }     
        }
        
        //2.更新UI方法
        void UpadteUI(string msg)
        {
            ShowMsg(msg);
        }


        //3.显示消息
        void ShowMsg(string msg)
        {
            txtMsg.AppendText(msg + "\r\n");
        }


       
    }




    /// <summary>
    /// 创建一个socketHelper类
    /// </summary>
    internal class SocketHelper
    {
        public static Socket CreateConnSocket(string ip, int port)
        {
            //创建一个连接套接字
            Socket socketConn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //创建一个IP地址
            IPAddress address = IPAddress.Parse(ip);
            //创建一个包含IP和端口的网络节点
            IPEndPoint endPoint = new IPEndPoint(address, port);
            //连接网络节点
            socketConn.Connect(endPoint);
            //返回监听套接字
            return socketConn;
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值