zoukankan      html  css  js  c++  java
  • TcpServer

      1     /// <summary>
      2     /// 从 TCP 网络客户端侦听连接。
      3     /// </summary>
      4     public class TcpServer
      5     {
      6         private bool isClosing = false; // 服务器 closing 标志位
      7         private TcpListener server; // 服务器的 TcpListener
      8         private Action<TcpConnection> connectinAction; // 每个客户端连接的委托
      9 
     10         /// <summary>
     11         /// 服务器运行状态
     12         /// </summary>
     13         public bool IsRunning { get; private set; }
     14 
     15         /// <summary>
     16         /// 使用指定的本地终结点初始化 TcpServer 类的新实例。
     17         /// </summary>
     18         /// <param name="localEP">将 TcpServer 绑定到的本地终结点。</param>
     19         public TcpServer(IPEndPoint localEP)
     20         {
     21             IsRunning = false;
     22             server = new TcpListener(localEP);
     23         }
     24 
     25         /// <summary>
     26         /// 初始化 TcpServer 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。
     27         /// </summary>
     28         /// <param name="localaddr">本地 IP 地址</param>
     29         /// <param name="port">用来侦听传入的连接尝试的端口。</param>
     30         public TcpServer(IPAddress localaddr, int port) : this(new IPEndPoint(localaddr, port))
     31         {
     32         }
     33 
     34         /// <summary>
     35         /// 初始化 TcpServer 类的新实例,该类在指定的本地 IP 地址和端口号上侦听是否有传入的连接尝试。
     36         /// </summary>
     37         /// <param name="address">本地 IP 地址字符串</param>
     38         /// <param name="port">用来侦听传入的连接尝试的端口。</param>
     39         public TcpServer(string address, int port) : this(IPAddress.Parse(address), port)
     40         {
     41         }
     42 
     43         /// <summary>
     44         /// 开始侦听传入的连接请求。
     45         /// </summary>
     46         /// <param name="action">每个客户端连接对应的委托</param>
     47         public void Start(Action<TcpConnection> action)
     48         {
     49             if (!IsRunning)
     50             {
     51                 IsRunning = true;
     52                 isClosing = false;
     53                 connectinAction = action;
     54                 server.Start();
     55                 StartAccept();
     56             }
     57         }
     58 
     59         /// <summary>
     60         /// 获取当前实例的基础 EndPoint。
     61         /// </summary>
     62         public EndPoint LocalEndPoint
     63         {
     64             get { return server.LocalEndpoint; }
     65         }
     66 
     67         /// <summary>
     68         /// 获取或设置一个 bool 值,该值指定该实例是否只允许一个基础套接字来侦听特定端口。
     69         /// </summary>
     70         public bool ExclusiveAddressUse
     71         {
     72             get { return server.ExclusiveAddressUse; }
     73             set { server.ExclusiveAddressUse = value; }
     74         }
     75 
     76         /// <summary>
     77         /// 获取基础网络 Socket
     78         /// </summary>
     79         public Socket Server
     80         {
     81             get { return server.Server; }
     82         }
     83 
     84         /// <summary>
     85         /// 关闭侦听器。
     86         /// </summary>
     87         public void Stop()
     88         {
     89             isClosing = true;
     90             server.Stop();
     91             IsRunning = false;
     92         }
     93 
     94         /// <summary>
     95         /// 开始一个异步操作来接受一个传入的连接尝试。
     96         /// </summary>
     97         private void StartAccept()
     98         {
     99             server.BeginAcceptTcpClient(iar =>
    100             {
    101                 try
    102                 {
    103                     if (isClosing) return;
    104                     TcpClient tcpClient = server.EndAcceptTcpClient(iar);
    105                     TcpConnection client = new TcpConnection(tcpClient, connectinAction);
    106                 }
    107                 catch (Exception ex)
    108                 {
    109                     Trace.TraceError(ex.Message);
    110                 }
    111                 StartAccept();
    112             }, null);
    113 
    114         }
    115 
    116         /// <summary>
    117         /// 返回指定终结点的 IP 地址和端口号。
    118         /// </summary>
    119         /// <returns>包含指定终结点(例如,192.168.1.2:80)的 IP 地址和端口号的字符串。</returns>
    120         public override string ToString()
    121         {
    122             return LocalEndPoint.ToString();
    123         }
    124 
    125         /// <summary>
    126         /// 获取本地IP地址
    127         /// </summary>
    128         /// <param name="addressFamily">地址族,默认IPv4</param>
    129         /// <returns>本地IP地址数组</returns>
    130         public static IPAddress[] GetLocalAddress(AddressFamily addressFamily = AddressFamily.InterNetwork)
    131         {
    132             List<IPAddress> list = new List<IPAddress>();
    133             list.Add(IPAddress.Any);
    134             list.Add(IPAddress.Loopback);
    135             foreach (var item in Dns.GetHostAddresses(Dns.GetHostName()))
    136             {
    137                 if (item.AddressFamily == addressFamily)
    138                     list.Add(item);
    139             }
    140             return list.ToArray();
    141         }
    142 
    143         /// <summary>
    144         /// 获取正在使用中的端口
    145         /// </summary>
    146         /// <param name="address">指定IP地址,默认全部地址</param>
    147         /// <returns>正在使用中的端口数组</returns>
    148         public static int[] GetInUsedPort(string address = null)
    149         {
    150             List<IPEndPoint> localEP = new List<IPEndPoint>();
    151             List<int> localPort = new List<int>();
    152             IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
    153             localEP.AddRange(ipGlobalProperties.GetActiveTcpListeners());
    154             localEP.AddRange(ipGlobalProperties.GetActiveUdpListeners());
    155             localEP.AddRange(ipGlobalProperties.GetActiveTcpConnections().Select(item => item.LocalEndPoint));
    156             foreach (var item in localEP.Distinct())
    157             {
    158                 if (address == null || item.Address.ToString() == address)
    159                     localPort.Add(item.Port);
    160             }
    161             localPort.Sort();
    162             return localPort.Distinct().ToArray();
    163         }
    164 
    165         /// <summary>
    166         /// 随机获取一个大于等于 min 的空闲端口
    167         /// </summary>
    168         /// <param name="min">指定最小空闲端口,默认1024</param>
    169         /// <param name="address">指定IP地址,默认全部地址</param>
    170         /// <returns>一个指定IP地址与范围的随机空闲端口</returns>
    171         public static int GetFreePort(int min = 1024, string address = null)
    172         {
    173             int freePort = -1;
    174             Random random = new Random();
    175             int[] freePorts = GetInUsedPort(address)
    176                 .Where(x => x >= (min = min <= 0 ? 1 : min))
    177                 .ToArray();
    178             while (freePort < 0)
    179             {
    180                 freePort = random.Next(min, 65536);
    181                 foreach (var item in freePorts)
    182                 {
    183                     if (freePort == item)
    184                         freePort = -1;
    185                 }
    186             }
    187             return freePort;
    188         }
    189     }
    190 
    191     /// <summary>
    192     /// TCP 网络服务的一个客户端连接。
    193     /// </summary>
    194     public class TcpConnection : IDisposable
    195     {
    196         private bool isClosing = false; // 当前连接 closing 标志位
    197         private byte[] receiveBuffer; // 当前连接用于 receive 的数据缓冲区
    198         private TcpClient tcpClient; // 当前连接的 TcpClient 对象
    199         private NetworkStream networkStream; // 当前连接的 NetworkerStream 对象
    200         private readonly Action<TcpConnection> initialize; // 当前连接的委托
    201 
    202         /// <summary>
    203         /// 通过指定的 TcpClient 和 Action 实例化一个与服务器建立连接的客户端。
    204         /// </summary>
    205         /// <param name="client">指定的 TcpClient</param>
    206         /// <param name="action">客户端委托</param>
    207         public TcpConnection(TcpClient client, Action<TcpConnection> action)
    208         {
    209             tcpClient = client;
    210             initialize = action;
    211             networkStream = tcpClient.GetStream();
    212             RemoteEndPoint = CopyEndPoint(tcpClient.Client.RemoteEndPoint);
    213             OnAccept = x => { };
    214             OnReceive = (x, y) => { };
    215             OnClose = (x, y) => { };
    216             OnError = (x, y) => { };
    217             initialize(this);
    218             OnAccept(this);
    219             receiveBuffer = new byte[tcpClient.ReceiveBufferSize];
    220             StartReceive();
    221         }
    222 
    223         /// <summary>
    224         /// 获取 EndPoint 的深拷贝
    225         /// </summary>
    226         /// <param name="endPoint">需要拷贝的源对象</param>
    227         /// <returns>目标副本</returns>
    228         private EndPoint CopyEndPoint(EndPoint endPoint)
    229         {
    230             return endPoint.Create(endPoint.Serialize());
    231         }
    232 
    233         /// <summary>
    234         /// 从当前连接开始异步读取。
    235         /// </summary>
    236         private void StartReceive()
    237         {
    238             try
    239             {
    240                 networkStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, iar =>
    241                 {
    242                     try
    243                     {
    244                         if (isClosing) return;
    245                         int size = networkStream.EndRead(iar);
    246                         if (size == 0 || !tcpClient.Connected || !networkStream.CanRead)
    247                         {
    248                             Close(true);
    249                             return;
    250                         }
    251                         byte[] data = new byte[size];
    252                         Buffer.BlockCopy(receiveBuffer, 0, data, 0, size);
    253                         OnReceive(this, data);
    254                     }
    255                     catch (Exception ex)
    256                     {
    257                         OnError(this, ex);
    258                     }
    259                     StartReceive();
    260                 }, null);
    261             }
    262             catch (Exception ex)
    263             {
    264                 OnError(this, ex);
    265             }
    266         }
    267 
    268         /// <summary>
    269         /// 获取或设置一个值,该值在发送或接收缓冲区未满时禁用延迟。
    270         /// </summary>
    271         public bool NoDelay
    272         {
    273             get { return tcpClient.NoDelay; }
    274             set { tcpClient.NoDelay = value; }
    275         }
    276 
    277         /// <summary>
    278         /// 获取已经从网络接收且可供读取的数据量。
    279         /// </summary>
    280         public int Available
    281         {
    282             get { return tcpClient.Available; }
    283         }
    284 
    285         /// <summary>
    286         /// 获取或设置基础 Socket。
    287         /// </summary>
    288         public Socket Client
    289         {
    290             get { return tcpClient.Client; }
    291         }
    292 
    293         /// <summary>
    294         /// 获取当前连接的终节点
    295         /// </summary>
    296         public EndPoint RemoteEndPoint
    297         {
    298             get; private set;
    299         }
    300 
    301         /// <summary>
    302         /// 获取一个 bool 值,该值指示 Socket 是否已连接到远程主机。
    303         /// </summary>
    304         public bool Connected
    305         {
    306             get { return tcpClient.Connected; }
    307         }
    308 
    309         /// <summary>
    310         /// 获取或设置 bool 值,该值指定是否只允许一个客户端使用端口。
    311         /// </summary>
    312         public bool ExclusiveAddressUse
    313         {
    314             get { return tcpClient.ExclusiveAddressUse; }
    315             set { tcpClient.ExclusiveAddressUse = value; }
    316         }
    317 
    318         /// <summary>
    319         /// 开始向当前连接异步写入。
    320         /// </summary>
    321         /// <param name="data">类型 Byte 的数组,该数组包含要写入的数据。</param>
    322         /// <returns></returns>
    323         public IAsyncResult Send(byte[] data)
    324         {
    325             try
    326             {
    327                 return networkStream.BeginWrite(data, 0, data.Length, iar =>
    328                 {
    329                     try
    330                     {
    331                         if (!tcpClient.Connected || !networkStream.CanRead)
    332                         {
    333                             Close(true);
    334                             return;
    335                         }
    336                         networkStream.EndWrite(iar);
    337                     }
    338                     catch (Exception ex)
    339                     {
    340                         OnError(this, ex);
    341                     }
    342                 }, null);
    343             }
    344             catch (Exception ex)
    345             {
    346                 OnError(this, ex);
    347             }
    348             return null;
    349         }
    350 
    351         /// <summary>
    352         /// 开始向当前连接异步写入。
    353         /// </summary>
    354         /// <param name="message">该 string 包含要写入的数据。</param>
    355         /// <returns></returns>
    356         public IAsyncResult Send(string message)
    357         {
    358             return Send(Encoding.UTF8.GetBytes(message));
    359         }
    360 
    361         /// <summary>
    362         /// 关闭基础连接并释放所有资源
    363         /// </summary>
    364         /// <param name="activeExit">是否主动关闭</param>
    365         private void Close(bool activeExit)
    366         {
    367             isClosing = true;
    368             networkStream.Close();
    369             tcpClient.Close();
    370             OnClose(this, activeExit);
    371         }
    372 
    373         /// <summary>
    374         /// 关闭基础连接并释放所有资源
    375         /// </summary>
    376         public void Close()
    377         {
    378             Close(false);
    379         }
    380 
    381         /// <summary>
    382         /// 关闭基础连接并释放所有资源,和Close()效果相同
    383         /// </summary>
    384         public void Dispose()
    385         {
    386             Close(false);
    387         }
    388 
    389         /// <summary>
    390         /// 返回指定终结点的 IP 地址和端口号。
    391         /// </summary>
    392         /// <returns>包含指定终结点(例如,192.168.1.2:80)的 IP 地址和端口号的字符串。</returns>
    393         public override string ToString()
    394         {
    395             return RemoteEndPoint.ToString();
    396         }
    397 
    398         /// <summary>
    399         /// TcpConnection 的 accept 事件
    400         /// </summary>
    401         public Action<TcpConnection> OnAccept { get; set; }
    402 
    403         /// <summary>
    404         /// TcpConnection 的 receive 事件
    405         /// </summary>
    406         public Action<TcpConnection, byte[]> OnReceive { get; set; }
    407 
    408         /// <summary>
    409         /// TcpConnection 的 close 事件
    410         /// </summary>
    411         public Action<TcpConnection, bool> OnClose { get; set; }
    412 
    413         /// <summary>
    414         /// TcpConnection 的 error 事件
    415         /// </summary>
    416         public Action<TcpConnection, Exception> OnError { get; set; }
    417 
    418     }
  • 相关阅读:
    从头认识java-5.2 包(package)
    从头认识java-5.1 为什么需要访问权限?
    从头认识java-4.9 枚举类型
    从头认识java-4.8 数组的初始化(2)-可变参数列表
    从头认识java-4.8 数组的初始化(1)
    从头认识java-4.7 构造器初始化(3)
    从头认识java-4.7 构造器初始化(2)
    全局对象:数字对象
    JavaScript的数据类型
    全局对象:String对象
  • 原文地址:https://www.cnblogs.com/baiqjh/p/11316551.html
Copyright © 2011-2022 走看看