1. 程式人生 > >C#網路程式設計系列文章(二)之Socket實現同步TCP伺服器

C#網路程式設計系列文章(二)之Socket實現同步TCP伺服器

原創性宣告

本文作者:小竹zz  本文地址http://blog.csdn.net/zhujunxxxxx/article/details/44258719 轉載請註明出處

文章系列目錄


本文介紹

在上一篇部落格中我說了,我將會介紹c#中使用Socket和TcpListener和UdpClient實現各種同步和非同步的TCP和UDP伺服器,這些都是是我自己花了很多天的時間來總結的,這樣一來相信剛接觸c#網路程式設計的朋友們不會像以前的我一樣到處出找資料,到處除錯。本次我介紹的是使用Socket來實現的同步的TCP伺服器,同步的TCP伺服器和第一篇裡面介紹的非同步TCP伺服器的區別就是,在Socket呼叫Accept的時候是否會阻塞。

同步的TCP伺服器在接受到一個客戶端的請求的時候一般是開啟一個執行緒去處理和這個客戶端的通訊工作,這種方式的不好的地方就是資源消耗大,但是假如使用執行緒池的話,事先分配一定數量的執行緒,效能還是可以。

但是我之所以給出很多方法實現的伺服器,主要目的就是想測試哪種情況效能比較不錯,就目前來看,非同步的模式比較NICE,但是其實還有一種IOCP模式效能是最好的。。。

Socket同步TCP伺服器

服務端程式碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace NetFrame.Net.TCP.Sock.Synchronous
{
    /// <summary>
    /// 基於socket實現的同步TCP伺服器
    /// </summary>
    public class SocketTCPServer
    {
        #region Fields
        /// <summary>
        /// 伺服器程式允許的最大客戶端連線數
        /// </summary>
        private int _maxClient;

        /// <summary>
        /// 當前的連線的客戶端數
        /// </summary>
        private int _clientCount;

        /// <summary>
        /// 伺服器使用的非同步socket
        /// </summary>
        private Socket _serverSock;

        /// <summary>
        /// 客戶端會話列表
        /// </summary>
        private List<SocketClientHandle> _clients;

        private bool disposed = false;

        #endregion

        #region Properties

        /// <summary>
        /// 伺服器是否正在執行
        /// </summary>
        public bool IsRunning { get; private set; }
        /// <summary>
        /// 監聽的IP地址
        /// </summary>
        public IPAddress Address { get; private set; }
        /// <summary>
        /// 監聽的埠
        /// </summary>
        public int Port { get; private set; }
        /// <summary>
        /// 通訊使用的編碼
        /// </summary>
        public Encoding Encoding { get; set; }


        #endregion

        #region 建構函式
        /// <summary>
        /// 同步Socket TCP伺服器
        /// </summary>
        /// <param name="listenPort">監聽的埠</param>
        public SocketTCPServer(int listenPort)
            : this(IPAddress.Any, listenPort, 1024)
        {
        }

        /// <summary>
        /// 同步Socket TCP伺服器
        /// </summary>
        /// <param name="localEP">監聽的終結點</param>
        public SocketTCPServer(IPEndPoint localEP)
            : this(localEP.Address, localEP.Port, 1024)
        {
        }

        /// <summary>
        /// 同步Socket TCP伺服器
        /// </summary>
        /// <param name="localIPAddress">監聽的IP地址</param>
        /// <param name="listenPort">監聽的埠</param>
        /// <param name="maxClient">最大客戶端數量</param>
        public SocketTCPServer(IPAddress localIPAddress, int listenPort, int maxClient)
        {
            this.Address = localIPAddress;
            this.Port = listenPort;
            this.Encoding = Encoding.Default;

            _maxClient = maxClient;
            _clients = new List<SocketClientHandle>();
            _serverSock = new Socket(localIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        }

        #endregion

        #region Method
        /// <summary>
        /// 啟動伺服器
        /// </summary>
        public void Start()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                _serverSock.Bind(new IPEndPoint(this.Address, this.Port));
                Thread thread = new Thread(StartListen);
                thread.Start();

            }
        }
        /// <summary>
        /// 開始進行監聽
        /// </summary>
        private void StartListen()
        {
            _serverSock.Listen(1024);
            SocketClientHandle handle;
            while (IsRunning)
            {
                if (_clientCount >= _maxClient)
                {
                    //TODO 客戶端過多異常
                    RaiseOtherException(null);
                }
                else
                {
                    Socket clientSock = _serverSock.Accept();
                    _clientCount++;
                    //TODO 建立一個處理客戶端的執行緒並啟動
                    handle = new SocketClientHandle(clientSock);
                    _clients.Add(handle);
                    //使用執行緒池來操作
                    ThreadPool.QueueUserWorkItem(new WaitCallback(handle.RecevieData));

                    //Thread pthread;
                    //pthread = new Thread(new ThreadStart(client.RecevieData));
                    //pthread.Start();
                    //這裡應該使用執行緒池來進行
                }
            }

        }
        /// <summary>
        /// 停止伺服器
        /// </summary>
        public void Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;
                _serverSock.Close();
                //TODO 關閉對所有客戶端的連線

            }
        }
        /// <summary>
        /// 傳送函式
        /// </summary>
        public void Send(string msg, SocketClientHandle client)
        {
            //TODO
        }

        /// <summary>
        /// 關閉一個與客戶端之間的會話
        /// </summary>
        /// <param name="handle">需要關閉的客戶端會話物件</param>
        public void Close(SocketClientHandle handle)
        {
            if (handle != null)
            {
                _clients.Remove(handle);
                handle.Dispose();
                _clientCount--;
                //TODO 觸發關閉事件
                
            }
        }
        /// <summary>
        /// 關閉所有的客戶端會話,與所有的客戶端連線會斷開
        /// </summary>
        public void CloseAllClient()
        {
            foreach (SocketClientHandle handle in _clients)
            {
                Close(handle);
            }
            _clientCount = 0;
            _clients.Clear();
        }

        #endregion

        #region 事件

        /// <summary>
        /// 與客戶端的連線已建立事件
        /// </summary>
        public event EventHandler<SocketEventArgs> ClientConnected;
        /// <summary>
        /// 與客戶端的連線已斷開事件
        /// </summary>
        public event EventHandler<SocketEventArgs> ClientDisconnected;

        /// <summary>
        /// 觸發客戶端連線事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseClientConnected(SocketClientHandle handle)
        {
            if (ClientConnected != null)
            {
                ClientConnected(this, new SocketEventArgs(handle));
            }
        }
        /// <summary>
        /// 觸發客戶端連線斷開事件
        /// </summary>
        /// <param name="client"></param>
        private void RaiseClientDisconnected(Socket client)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, new SocketEventArgs("連線斷開"));
            }
        }

        /// <summary>
        /// 接收到資料事件
        /// </summary>
        public event EventHandler<SocketEventArgs> DataReceived;

        private void RaiseDataReceived(SocketClientHandle handle)
        {
            if (DataReceived != null)
            {
                DataReceived(this, new SocketEventArgs(handle));
            }
        }

        /// <summary>
        /// 資料傳送事件
        /// </summary>
        public event EventHandler<SocketEventArgs> CompletedSend;

        /// <summary>
        /// 觸發資料傳送事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseCompletedSend(SocketClientHandle handle)
        {
            if (CompletedSend != null)
            {
                CompletedSend(this, new SocketEventArgs(handle));
            }
        }


        /// <summary>
        /// 網路錯誤事件
        /// </summary>
        public event EventHandler<SocketEventArgs> NetError;
        /// <summary>
        /// 觸發網路錯誤事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseNetError(SocketClientHandle handle)
        {
            if (NetError != null)
            {
                NetError(this, new SocketEventArgs(handle));
            }
        }

        /// <summary>
        /// 異常事件
        /// </summary>
        public event EventHandler<SocketEventArgs> OtherException;
        /// <summary>
        /// 觸發異常事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseOtherException(SocketClientHandle handle, string descrip)
        {
            if (OtherException != null)
            {
                OtherException(this, new SocketEventArgs(descrip, handle));
            }
        }
        private void RaiseOtherException(SocketClientHandle handle)
        {
            RaiseOtherException(handle, "");
        }

        #endregion

        #region Close 未實現
        #endregion

        #region 釋放
        /// <summary>
        /// Performs application-defined tasks associated with freeing, 
        /// releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release 
        /// both managed and unmanaged resources; <c>false</c> 
        /// to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    try
                    {
                        Stop();
                        if (_serverSock != null)
                        {
                            _serverSock = null;
                        }
                    }
                    catch (SocketException)
                    {
                        //TODO 異常
                    }
                }
                disposed = true;
            }
        }
        #endregion
    }
}

對客戶端操作封裝的Handle類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace NetFrame.Net.TCP.Sock.Synchronous
{
    /// <summary>
    /// Socket 伺服器用於處理客戶端連線封裝的客戶端處理類
    /// </summary>
    public class SocketClientHandle:IDisposable
    {
        /// <summary>
        /// 與客戶端相關聯的socket
        /// </summary>
        private Socket _client;

        /// <summary>
        /// 標識是否與客戶端相連線
        /// </summary>
        private bool _is_connect;
        public bool IsConnect
        {
            get { return _is_connect; }
            set { _is_connect = value; }
        }

        /// <summary>
        /// 資料接受緩衝區
        /// </summary>
        private byte[] _recvBuffer;

        public SocketClientHandle(Socket client)
        {
            this._client = client;
            _is_connect = true;
            _recvBuffer = new byte[1024 * 1024 * 2];
        }

        #region Method
        /// <summary>
        /// 接受來自客戶端發來的資料
        /// </summary>
        public void RecevieData(Object state)
        {
            int len = -1;
            while (_is_connect)
            {
                try
                {
                    len = _client.Receive(_recvBuffer);
                }
                catch (Exception)
                {
                    //TODO
                }
            }
        }

        /// <summary>
        /// 向客戶端傳送資料
        /// </summary>
        public void SendData(string msg)
        {
            byte[] data = Encoding.Default.GetBytes(msg);
            try
            {
                //有一種比較好的寫法
                _client.Send(data);
            }
            catch (Exception)
            {
                //TODO 處理異常
            }
        }

        #endregion


        #region 事件


        //TODO 訊息傳送事件
        //TODO 資料收到事件
        //TODO 異常處理事件

        #endregion

        #region 釋放
        /// <summary>
        /// Performs application-defined tasks associated with freeing, 
        /// releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            _is_connect = false;
            if (_client != null)
            {
                _client.Close();
                _client = null;
            }
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}

Socket同步TCP伺服器的時間引數類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetFrame.Net.TCP.Sock.Synchronous
{
    /// <summary>
    /// 同步Socket TCP伺服器事件類
    /// </summary>
    public class SocketEventArgs : EventArgs
    {
        /// <summary>
        /// 提示資訊
        /// </summary>
        public string _msg;

        /// <summary>
        /// 客戶端狀態封裝類
        /// </summary>
        public SocketClientHandle _handle;

        /// <summary>
        /// 是否已經處理過了
        /// </summary>
        public bool IsHandled { get; set; }

        public SocketEventArgs(string msg)
        {
            this._msg = msg;
            IsHandled = false;
        }
        public SocketEventArgs(SocketClientHandle handle)
        {
            this._handle = handle;
            IsHandled = false;
        }
        public SocketEventArgs(string msg, SocketClientHandle handle)
        {
            this._msg = msg;
            this._handle = handle;
            IsHandled = false;
        }
    }
}

本文作者:小竹zz  本文地址http://blog.csdn.net/zhujunxxxxx/article/details/44258719 轉載請註明出處