C#之Socket通信


0.雖然之前在項目中也有用過Socket,但始終不是自己搭建的,所以對Server,Clinet端以及心跳,斷線重連總沒有很深入的理解,現在自己搭建了一遍加深一下理解。

服務端使用WPF界面,客戶端使用控制台。實現了心跳,斷線重連,一個服務端對應多個客戶端的功能。

一.服務端

1.1 先創建一個Socket實例,並綁定到20000端口號;通過Listen方法開始監聽並設置最大監聽數量。

//新建一個Socket服務端實例,並綁定到20000端口
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketServer.Bind(new IPEndPoint(IPAddress.Any, 20000));

//設置最大監聽數量
socketServer.Listen(10);

1.2 當有客戶端成功連接,則會通過Accept方法生產一個新的Socket實例;此時可開啟心跳定時器,並開啟一個線程接收消息。

//開啟一個線程監聽客戶端
Thread thd = new Thread(new ThreadStart(ListenSocket));
thd.Start();

/// <summary>
/// 開始監聽
/// </summary>
private void ListenSocket()
{
    try
    {
        while (true)
        {
            Socket _socket = socketServer.Accept();

            //開始心跳
            System.Timers.Timer heartbeatTimer = new System.Timers.Timer();
            heartbeatTimer.Interval = 60000;
            heartbeatTimer.Elapsed += new System.Timers.ElapsedEventHandler((s, e) => heartbeatTimerIsUp(s, e, _socket));
            heartbeatTimer.Start();

            Thread thdReceive = new Thread(new ParameterizedThreadStart(thdRevMethod));
            thdReceive.Start(_socket);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("程序出現異常:" + ex);
    }
}

1.3 接收消息與發送消息的代碼如下

/// <summary>
/// 接收消息
/// </summary>
/// <param name="obj"></param>
private void thdRevMethod(object obj)
{
    Socket _socket = obj as Socket;
    try
    {
        while (true)
        {
            byte[] resByte = new byte[1024];
            int resInt = _socket.Receive(resByte);
            if (resInt > 0)
            {
                string res = Encoding.Default.GetString(resByte, 0, resInt);///接收消息后操作
            }
        }
    }
    catch (SocketException sex)
    {
        if (sex.SocketErrorCode == SocketError.ConnectionReset)
        {
            //當客戶端斷開連接,從客戶端列表中移除該客戶端
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("程序出現異常:" + ex);
    }
}

/// <summary>
/// 發送消息
/// </summary>
/// <param name="msg"></param>
/// <param name="ipAndPord"></param>
public bool sendMsg(string msg,string ipAndPord)
{
    try
    {
        Socket _socket = socketTimerDic.SingleOrDefault(r => string.Equals(r.Key.RemoteEndPoint.ToString(), ipAndPord)).Key;
        if (_socket != null)
        {
            byte[] byteStr = Encoding.Default.GetBytes(msg);
            _socket.Send(byteStr);
            return true;
        }
        else
        {
            return false;
        }
    }
    catch (Exception)
    {
        return false;
    }
}

二.客戶端

2.1 先創建一個Socket實例,並連接到服務端所在的ip端口;連接成功后開啟心跳定會器並開啟發送和接收消息的兩個線程。

// 新建客戶端實例,並連接到服務端所在的端口號
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

socketClient.Connect("127.0.0.1", 20000);
Console.WriteLine("成功連接到服務端");
heartbeatTimer.Start();

//開啟一個線程發送消息
Thread thdSend = new Thread(new ThreadStart(thdSendMethod));
thdSend.Start();

//開啟一個線程接收信息
Thread thdRev = new Thread(new ThreadStart(thdRevMethod));
thdRev.Start();

2.2 發送消息和接收消息的代碼如下,當服務端斷開會在接收消息的線程中觸發sex.SocketErrorCode == SocketError.ConnectionReset的異常,此時捕獲到后開啟重連定時器即可

/// <summary>
/// 接收消息
/// </summary>
private static void thdRevMethod()
{
    try
    {
        while (true)
        {
            byte[] resByte = new byte[1024];
            int resInt = socketClient.Receive(resByte);
            if (resInt > 0)
            {
                 Console.WriteLine(Encoding.Default.GetString(resByte,0, resInt));
            }
        }

    }
    catch (SocketException sex)
    {
        if (sex.SocketErrorCode == SocketError.ConnectionReset)
        {
            Console.WriteLine("服務端斷開!5s后重連!");
            reconnectTimer.Start();
            heartbeatTimer.Stop();
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("程序出現異常:" + ex);
        heartbeatTimer.Stop();
    }
}

//發送信息
private static void thdSendMethod()
{
    try
    {
        while (true)
        {
            string res = Console.ReadLine();
            byte[] resByte = Encoding.Default.GetBytes(res);
            socketClient.Send(resByte);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("程序出現異常:" + ex);
        heartbeatTimer.Stop();
    }
}    

三.運行示例

四..總結

假如代碼中有錯誤的地方,希望可以幫忙指出來改之。

其中要注意的地方如下:
1.WPF關閉窗口后,需要通過 Environment.Exit(0);來結束掉當前整個服務端進程,否則Socket開啟的接收和發送進程將還在,客戶端也不會檢測到服務端斷開。

2.在SocketException中捕獲異常sex.SocketErrorCode == SocketError.ConnectionReset時說明客戶端/服務端連接斷開了,需要進行重連等操作。

3.在接收到消息時候需要用GetString(byte[] bytes, int index, int count)指定長度,而不該使用GetString(byte[] bytes),否則可能出現很多空格。

4.定時器假如需要傳遞參數,可以通過設置全局變量,或者(s, e) => heartbeatTimerIsUp(s, e, _socket)單獨傳遞一個變量到方法中。

源碼下載地址如下:SocketDemo.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM