1. 程式人生 > >C#編寫的區域網聊天工具(本菜鳥剛學Socket,拿來分享下~~~)

C#編寫的區域網聊天工具(本菜鳥剛學Socket,拿來分享下~~~)

class Server //服務端方法
    {
        #region 繫結IP、埠
        IniOperate io = new IniOperate("C:\\lwj.ini");
        public Socket Bind()
        {
            Socket sl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep=new IPEndPoint(IPAddress.Parse(Service.GetLocatIP()),int.Parse( io.GetIni("埠設定","port","5697")));//繫結的IP與埠
            
            if (!sl.Connected)
            {
                try
                {
                    sl.Bind(iep);
                    sl.Listen(100);
                }
                catch
                {
                    sl = null;
                }
            }
            return sl;
        }
        #endregion


        public byte[] sendMsg(string msg)
        {
            byte[] msgArr = System.Text.Encoding.UTF8.GetBytes(msg);
            return msgArr;
        }
    }

class Service//獲取IP
    {
        private static string IP;
        public static string GetLocatIP()
        {
            foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)//遍歷當前主機IP協議簇
            {
                if (_IPAddress.AddressFamily.ToString() == "InterNetwork")//如果協議名為"InterNetwork",則表明是本機IP地址
                {
                    IP = _IPAddress.ToString();
                }
            }
            return IP;
        }
    }

class IniOperate//操作配置檔案方法  INI格式
    {
        [DllImport("kernel32")]
        private static extern bool WritePrivateProfileString(string section, string key, string value, string fileName);//必須有static、extern,方法名必須與所呼叫的屬性類中方法名相同,引數型別必須與元方法相同
        [DllImport("kernel32")]
        private static extern bool GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int valSize, string fileName);

        public IniOperate(string iniUrl) { url = iniUrl; }

        private string url;
        private StringBuilder strB=new StringBuilder(1024);//設定一個接受資料的容器
        private const int size = 1024;//設定容器的大小

        #region 寫入資料
        public bool SetIni(string node, string key, string value)
        {
            return WritePrivateProfileString(node, key, value, url);
        }
        #endregion

        #region 讀取資料
        public string GetIni(string node, string key,string defaultValue)
        {
            GetPrivateProfileString(node, key, defaultValue, strB, size,url);
            return strB.ToString();
        }
        #endregion

        #region 刪除檔案或資料
        public string Delete()//刪檔案
        {
            if (File.Exists(url))
            {
                File.Delete(url);
                return "刪除成功";
            }
            else
            {
                return "指定的檔案不存在";
            }
        }
        public void Delete(string node,string key)//刪指定節點下的鍵
        {
            WritePrivateProfileString(node, key, null, url);
        }
        public void Delete(string node)//刪指定的節點
        {
            WritePrivateProfileString(node, null, null, url);
        }
        #endregion

    }




public partial class LWJ : Form//主程式方法
    {
        public LWJ()
        {
            InitializeComponent();
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//關閉對窗體上控制元件的跨執行緒檢查
        }
        Server s = new Server();
        IniOperate io = new IniOperate("C:\\lwj.ini");
        Socket sL;//用於監聽的套接字
        Socket sClient;//用與服務的套接字
        int Acount;//用來計算當前連線者的總數
        Dictionary<string, Socket> dic = new Dictionary<string, Socket>();//用於儲存請求者的ip與埠
        private void Form1_Load(object sender, EventArgs e)
        {
            if (!File.Exists("C:\\lwj.ini"))
            {
                io.SetIni("埠設定", "port", "5697");//寫一個初始的埠
            }
            sL = s.Bind();
            if (sL!=null)
            {
                Thread t = new Thread(fun1);
                t.IsBackground = true;
                t.Priority = ThreadPriority.AboveNormal;
                t.Start();//用一個執行緒啟動持續監聽

                Thread t2 = new Thread(forIp);
                t2.IsBackground = true;
                t2.Start();//用一個執行緒啟動遍歷當前區域網內開啟指定埠主機然後建立連線的方法
            }
            else
            {
                MessageBox.Show("本程式所需埠已被其他程式佔用~~~,請檢查您的'" + io.GetIni("埠設定", "port", "5697") + "埠'!!!或者開啟C:\\ipe.ini檔案,更改埠號\\(≧▽≦)/");
                this.Close();
            }
        }

        private void Form1_Activated(object sender, EventArgs e)
        {
            msg.Focus();//在窗體獲得焦點時  把焦點給指定控制元件
        }

        #region 持續監聽請求的連線
        void fun1()
        {
            while (true)
            {
                sClient = sL.Accept();
                Thread t3 = new Thread(getMsg);
                t3.IsBackground = true;
                t3.Start();
            }
        }
        #endregion

        #region 接收資訊
        void getMsg()
        {
            while (true)
            {
                try
                {
                    while (true)
                    {
                        byte[] serverMsg = new byte[1024 * 1024*5];//定義一個接收資訊的指定大小的快取區(byte陣列)
                        int msgCount=sClient.Receive(serverMsg);
                        if (serverMsg[0]==1)//判斷接收的資料時資訊還是檔案
                        {
                            if (MessageBox.Show(sClient.RemoteEndPoint.ToString().Split(':')[0] + "傳來一個檔案:", "是否接收", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
                            {
                                SaveFileDialog sf = new SaveFileDialog();
                                if (sf.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
                                {
                                    string saveUrl = sf.FileName;
                                    using (FileStream fs = new FileStream(saveUrl, FileMode.Create))
                                    {
                                        fs.Write(serverMsg, 1, msgCount - 1);
                                        MessageBox.Show("檔案儲存成功~");
                                    }
                                }
                            }
                        }
                        else
                        {
                            string context = System.Text.Encoding.UTF8.GetString(serverMsg, 0, msgCount);
                            richTextBox1.AppendText(sClient.RemoteEndPoint.ToString().Split(':')[0] + ":" + context + "\r\n");
                        }
                    }
                }
                catch(Exception)//發信息方斷開連線
                {
                    Acount -= 1;
                    label4.Text = Acount.ToString();
                    dic.Remove(this.sClient.RemoteEndPoint.ToString().Split(':')[0]);
                    listBox1.Items.Remove(this.sClient.RemoteEndPoint.ToString().Split(':')[0]);
                    break;
                }
            }
        }
        #endregion
        
        /*傳送訊息*/
        private void button2_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedItem==null)
            {
                MessageBox.Show("請選擇接收的使用者");
            }
            else if (msg.Text=="")
            {
                MessageBox.Show("請輸入傳送內容");
            }
            else
            {
                dic[listBox1.SelectedItem.ToString()].Send(s.sendMsg(msg.Text));//傳送訊息
                richTextBox1.AppendText("Me:" + msg.Text+"\r\n");
                msg.Text = "";
            }
        }
        private void msg_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Link;//獲取拖動檔案的路徑賦給目標區
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        /*傳送檔案*/
        private void msg_DragDrop(object sender, DragEventArgs e)
        {
            if (listBox1.SelectedItem==null)
            {
                MessageBox.Show("請先選擇接收檔案的使用者~");
            }
            else
            {
                string fileUrl= ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();//把文字框得到的路徑賦給指定的字元創供傳送檔案用
                using (FileStream fs=new FileStream(fileUrl,FileMode.Open))
                {
                    byte[] arrB = new byte[1024 * 1024 * 5];//定義傳輸檔案的快取區
                    int a = fs.Read(arrB, 0, arrB.Length);//把檔案以二進位制的方式讀到陣列中
                    byte[] arrSendFile = new byte[a + 1];//設定陣列大小
                    arrSendFile[0] = 1;//放入傳送資料的標識
                    Buffer.BlockCopy(arrB, 0, arrSendFile, 1, a);/*將arrB中資料複製到arrSendFile陣列中,與上迴圈效果相同*/
                    dic[listBox1.SelectedItem.ToString()].Send(arrSendFile);//傳送檔案到伺服器
                }
            }
        }

        #region 遍歷區域網
        public void forIp()
        {
            while (true)
            {
                for (int i = 2; i <= 255; i++)
                {
                    string pingIP = "192.168.1." + i.ToString();
                    if (dic.ContainsKey(pingIP) || Service.GetLocatIP()==pingIP)
                    {
                        continue;//如果所Ping的IP是本機則退出本次迴圈
                    }
                    Ping myPing = new Ping();
                    myPing.PingCompleted += new PingCompletedEventHandler(addItem);
                    myPing.SendAsync(pingIP,1,1);//引數:IP地址、傳送資料包大小單位是位元組、返回資料包的大小
                }
                Thread.Sleep(3000);//隔3秒迴圈一次區域網,用於發現新的使用者
            }
        }
        #endregion

        #region 判斷是否能ping通IP與埠
        public void addItem(object sender, PingCompletedEventArgs e)
        {
            if (e.Reply.Status == IPStatus.Success)
            {
                string rsl = e.Reply.Address.ToString();//得到Ping通的IP
                try
                {
                    Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPEndPoint iep = new IPEndPoint(IPAddress.Parse(rsl), int.Parse(io.GetIni("埠設定", "port", "5697")));//繫結的IP與埠
                    socketSend.Connect(iep);
                    dic.Add(rsl, socketSend);
                    listBox1.Items.Add(rsl.ToString());
                    Acount += 1;
                    label4.Text = Acount.ToString();
                }
                catch { }//能ping通但打不開埠的主機
            }
        }
        #endregion

        /*群發訊息*/
        private void button1_Click(object sender, EventArgs e)
        {
            if (dic.Values.Count==0)
            {
                MessageBox.Show("當前沒有已連線的使用者!");
            }
            else if (msg.Text=="")
            {
                MessageBox.Show("請輸入內容!");
            }
            else
            {
                foreach (var ds in dic.Values)
                {
                    ds.Send(s.sendMsg(msg.Text));
                }
                richTextBox1.AppendText(msg.Text + "\r\n");
                msg.Text ="";
            }
        }
    }