天天看點

Winfrom使用Socket雙向通信 -- 聊天程式(用戶端、服務端) -- 01

使用Winfrom寫一個聊天軟體

  • ​​效果圖​​
  • ​​原理​​
  • ​​代碼實作​​
  • ​​服務端​​
  • ​​用戶端​​
  • ​​注意事項​​

效果圖

原理

代碼實作

服務端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ScoketServerTest
{
    public partial class Server : Form
    {
        public Server()
        {
            InitializeComponent();
        }
        /// <summary>
        /// socket套接字
        /// </summary>
        Socket socketServer = null;
        /// <summary>
        /// 用戶端
        /// </summary>
        Socket socketClienCon = null;
        /// <summary>
        /// 用戶端資訊
        /// </summary>
        string clientIpMsg = string.Empty;
        /// <summary>
        /// 伺服器是否關閉  false:開啟  true:已關閉
        /// </summary>
        bool isClose = false;

        private void Server_Load(object sender, EventArgs e)
        {
            // 擷取目前主機名
            this.textBox1.Text = System.Net.Dns.GetHostName();
            // 預設端口設定為33333
            this.portTex.Text = "33333";
            // 根據主機名擷取ip位址
            //注意擷取的是IPV4的位址,有些電腦如果第一個就是IPV4可以把索引'1'更換成'0'
            this.ipTex.Text = System.Net.Dns.GetHostEntry(this.textBox1.Text).AddressList[1].ToString();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            try
            {
                // 擷取輸入框的ip位址
                IPAddress ip = IPAddress.Parse(this.ipTex.Text.ToString().Trim());
                // 擷取端口号
                int port = int.Parse(this.portTex.Text.ToString().Trim());
                IPEndPoint ipe = new IPEndPoint(ip,port);
                socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // 綁定網絡終結點
                socketServer.Bind(ipe);
                // 設定監聽最大連接配接數
                socketServer.Listen(10);
                isClose = false;
                this.lstRev.Items.Add(string.Format("伺服器已開啟,開始監聽消息"));
                this.lstRev.Items.Add(string.Format("目前主機名:{0}", this.textBox1.Text));
                this.lstRev.Items.Add(string.Format("目前IP:{0}", System.Net.Dns.GetHostEntry(this.textBox1.Text).AddressList[0].ToString()));
               
                Thread clientThread = new Thread(ListenTh);  //建立一個線程,用于監聽用戶端連接配接
                clientThread.IsBackground = true;  //設定為背景線程
                clientThread.Start();  //開啟線程
                Thread revThread = new Thread(RevTh);  //建立一個線程,用于監聽用戶端發來的消息
                revThread.IsBackground = true;//設定為背景線程
                revThread.Start();  //開啟線程

            }
            catch (Exception ex)
            {

                string errorMsg = string.Format("伺服器開啟失敗,失敗原因:{0}", ex.Message.ToString());
                this.lstRev.Items.Add(errorMsg);
            }
        }


        /// <summary>
        /// 監聽用戶端
        /// </summary>
        private void ListenTh()
        {
            while (!isClose)
            {
                try
                {
                    socketClienCon = socketServer.Accept();  //為監聽到的用戶端建立新的套接字
                    IPAddress clientIp = (socketClienCon.RemoteEndPoint as IPEndPoint).Address;  //擷取連接配接的用戶端的IP位址
                    int clientPort = (socketClienCon.RemoteEndPoint as IPEndPoint).Port;  //擷取連接配接的用戶端的端口号
                    clientIpMsg = string.Format("用戶端IP:{0},端口号:{1}", clientIp, clientPort);
                    OnlineClient(clientIpMsg);  //線上使用者并列印到線上使用者視窗
                }
                catch (Exception ex)
                {
                    string error = string.Format("監聽用戶端資訊異常,錯誤原因:{0}", ex.Message.ToString());
                    Recevice(error);
                    break;
                }
            }
        }
        delegate void OnlineClientDele(string clientMsg);  //建立線上使用者委托方法
        /// <summary>
        /// 線上使用者
        /// </summary>
        /// <param name="clientMsg">線上使用者資訊</param>
        private void OnlineClient(string clientMsg)
        {
            if (this.InvokeRequired)
            {
                OnlineClientDele oel = new OnlineClientDele(OnlineClient);
                this.Invoke(oel, new object[] { clientMsg });
            }
            else
            {
                this.lstClient.TopIndex = this.lstClient.Items.Count - 1;
                this.lstClient.Items.Add(clientMsg);
            }
        }

        /// <summary>
        /// 接收用戶端消息
        /// </summary>
        private void RevTh()
        {
            while (!isClose)
            {
                if (socketClienCon != null)
                {
                    try
                    {
                        byte[] clientMsg = new byte[1024];  //建立接收消息緩存數組
                        int msgLen = socketClienCon.Receive(clientMsg);  //接收從用戶端發來的資料
                        if (msgLen == 0)
                        {
                            break;
                        }
                        else
                        {
                            string msg = string.Format("{0}【收到 ← 來自:{1}】:\n{2}", DateTime.Now, clientIpMsg, Encoding.UTF8.GetString(clientMsg, 0, msgLen));
                            Recevice(msg);  //接收消息并列印到對話視窗
                        }
                    }
                    catch (Exception ex)
                    {
                        string error = string.Format("接收用戶端消息異常,錯誤原因:{0}", ex.Message.ToString());
                        Recevice(error);
                        break;
                    }
                }
            }
        }
        delegate void ReceviceDele(string msgStr); //建立接收消息委托方法
        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="msg">用戶端發來的消息</param>
        private void Recevice(string msg)
        {
            if (this.InvokeRequired)
            {
                ReceviceDele rel = new ReceviceDele(Recevice);
                this.Invoke(rel, new object[] { msg });
            }
            else
            {
                this.lstRev.TopIndex = this.lstRev.Items.Count - 1;
                this.lstRev.Items.Add(msg);
            }
        }       
        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            string msg = this.txtSend.Text.ToString().Trim();
            byte[] msgByt = Encoding.UTF8.GetBytes(msg);
            socketClienCon.Send(msgByt);
            this.lstRev.Items.Add(string.Format("{0}【發送 → 至 {1}】:\n{2}", DateTime.Now, clientIpMsg, msg));
            this.txtSend.Text = "";
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            isClose = true;
            socketServer.Close();
        }
        private void btnClose_Click(object sender, EventArgs e)
        {
            isClose = true;
            socketServer.Close();
        }

       
    }
}      

用戶端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebspcketClientTest
{
    public partial class Client : Form
    {
        public Client()
        {
            InitializeComponent();
        }

        /// <summary>
        /// socket套接字
        /// </summary>
        Socket cilentSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        bool isClose = true;

        private void Client_Load(object sender, EventArgs e)
        {
            this.portTex.Text = "33333";
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            //擷取ip
            IPAddress ip = null;
            if (!string.IsNullOrEmpty(this.ipTex.Text.ToString().Trim()))
            {
                ip = IPAddress.Parse(this.ipTex.Text.ToString().Trim());
            }
            else if(!string.IsNullOrEmpty(this.texHostName.Text.ToString().Trim()))
            {
                IPAddress[] addressList = Dns.GetHostEntry(this.texHostName.Text.ToString().Trim()).AddressList;
                // 根據HostName 擷取IPV4位址
                string ipStr = addressList[0].ToString();
                if (ipStr.Length > 17)
                {
                    ipStr = addressList[1].ToString();
                }
                // IP位址

                ip = IPAddress.Parse(ipStr);
     
                this.ipTex.Text = ip.ToString();
            }
            else
            {
                MessageBox.Show("無法擷取主機位址!");
                this.richTextBox1.Text += "無法擷取主機位址!\n";
                return;
            }
             
            //擷取端口号
            IPEndPoint ep = new IPEndPoint(ip, int.Parse(this.portTex.Text));
            //ws = new WebSocketSharp.WebSocket("ws://" + this.texHostName.Text + ":" + this.portTex.Text);
            try
            {
                //ws.Connect();
                //建立連接配接
                cilentSocket.Connect(ep);
                this.richTextBox1.Text += "連接配接成功!\n";
                isClose = false;
                Thread th = new Thread(StartMsg);
                th.IsBackground = true;
                th.Start();
            }
            catch (Exception ex)
            {
                this.richTextBox1.Text += string.Format("連接配接失敗,原因{0}1\n", ex.ToString());
            }
        }

        // 監聽伺服器資訊
        public void StartMsg()
        {
            while (!isClose)
            {
                try
                {
                    //發送消息的大小
                    var bytes = new byte[1024 * 1024];
                    var mlength = cilentSocket.Receive(bytes);
                    //将位元組轉換為字元串
                    string msg = Encoding.UTF8.GetString(bytes, 0, mlength);
                    this.Invoke(new Action(() =>
                    {
                        this.richTextBox1.Text += string.Format("【收到 ← 來自:{0}】:\n{1}\n", DateTime.Now, msg);
                    }));
                }
                catch (Exception ex)
                {   
                    this.Invoke(new Action(() =>
                    {
                        this.richTextBox1.Text += string.Format("連接配接異常{0}\n", ex.ToString());
                        isClose = true;
                    }));
                }
                
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            //把要發送的字元串轉化為位元組
            var bytes = Encoding.UTF8.GetBytes(this.txtSend.Text.Trim());
            cilentSocket.Send(bytes);
            this.richTextBox1.Text += string.Format("{0}【發送 → 至 {1}】:\n{2}\n", DateTime.Now,this.ipTex.Text, this.txtSend.Text.ToString().Trim());
            this.txtSend.Text = "";

        }

        private void button1_Click(object sender, EventArgs e)
        {
            isClose = true;
        }
    }
}      

注意事項

  1. 代碼是直接可以直接使用的,但是注意這是兩個項目的代碼,是以需要建立兩個項目,一個用戶端一個服務端;
  2. 需要為按鈕綁定點選事件才可以使事件生效;
  3. 設定ip位址的時候要符合自己網段的ip,否則無法開啟伺服器,另外也可以使用​

    ​127.0.0.1​

    ​.
  4. 在本項目中用戶端與服務端發送消息使用的是位元組發送,注意發出的編碼與接收的編碼要一緻,否則無法識别.