C#之Socket通訊

0.雖然以前在項目中也有用過Socket,但始終不是本身搭建的,因此對Server,Clinet端以及心跳,斷線重連總沒有很深刻的理解,如今本身搭建了一遍加深一下理解。socket

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

一.服務端線程

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

//新建一個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實例;此時可開啓心跳定時器,並開啓一個線程接收消息。blog

//開啓一個線程監聽客戶端
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;
    }
}

二.客戶端ip

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

// 新建客戶端實例,並鏈接到服務端所在的端口號
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();
    }
}    

三.運行示例string

四..總結

假如代碼中有錯誤的地方,但願能夠幫忙指出來改之。

其中要注意的地方以下:
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

相關文章
相關標籤/搜索