You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
5.6 KiB

  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. namespace Apewer.Network
  5. {
  6. /// <summary>TCP 客户端。</summary>
  7. public class TcpClient : IDisposable
  8. {
  9. Socket _socket = null;
  10. /// <summary>套接字实例。</summary>
  11. public Socket Socket { get => _socket; }
  12. /// <summary>在线。</summary>
  13. public bool Online { get => Extensions.Online(_socket); }
  14. /// <summary>本地终结点。</summary>
  15. public IPEndPoint LocalEndPoint { get; private set; }
  16. /// <summary>远程终结点。</summary>
  17. public IPEndPoint RemoteEndPoint { get; private set; }
  18. /// <summary>启动客户端,并连接到服务端。</summary>
  19. /// <param name="ip">远程 IP 地址。</param>
  20. /// <param name="port">远程端口号。</param>
  21. /// <exception cref="ArgumentOutOfRangeException" />
  22. /// <exception cref="FormatException" />
  23. /// <exception cref="SocketException" />
  24. public TcpClient(string ip, int port) : this(new IPEndPoint(IPAddress.Parse(ip), port), 0) { }
  25. /// <summary>启动客户端,并连接到服务端。</summary>
  26. /// <param name="ip">远程 IP 地址。</param>
  27. /// <param name="port">远程端口号。</param>
  28. /// <param name="timeout">连接超时毫秒数。当达到指定时长,或达到系统默认时长时,将会发生超时异常。</param>
  29. /// <exception cref="ArgumentOutOfRangeException" />
  30. /// <exception cref="FormatException" />
  31. /// <exception cref="SocketException" />
  32. public TcpClient(string ip, int port, int timeout) : this(new IPEndPoint(IPAddress.Parse(ip), port), timeout) { }
  33. /// <summary>启动客户端,并连接到服务端。</summary>
  34. /// <param name="ip">远程 IP 地址。</param>
  35. /// <param name="port">远程端口号。</param>
  36. /// <exception cref="ArgumentNullException" />
  37. /// <exception cref="ArgumentOutOfRangeException" />
  38. /// <exception cref="SocketException" />
  39. public TcpClient(IPAddress ip, int port) : this(new IPEndPoint(ip, port), 0) { }
  40. /// <summary>启动客户端,并连接到服务端。</summary>
  41. /// <param name="ip">远程 IP 地址。</param>
  42. /// <param name="port">远程端口号。</param>
  43. /// <param name="timeout">连接超时毫秒数。当达到指定时长,或达到系统默认时长时,将会发生超时异常。</param>
  44. /// <exception cref="ArgumentNullException" />
  45. /// <exception cref="ArgumentOutOfRangeException" />
  46. /// <exception cref="SocketException" />
  47. public TcpClient(IPAddress ip, int port, int timeout) : this(new IPEndPoint(ip, port), timeout) { }
  48. /// <summary>启动客户端,并连接到服务端。</summary>
  49. public TcpClient(IPEndPoint endpoint) : this(endpoint, 0) { }
  50. /// <summary>启动客户端,并连接到服务端。</summary>
  51. /// <param name="endpoint">远程终结点。</param>
  52. /// <param name="timeout">连接超时毫秒数。当达到指定时长,或达到系统默认时长时,将会发生超时异常。</param>
  53. /// <exception cref="ArgumentNullException" />
  54. /// <exception cref="ArgumentOutOfRangeException" />
  55. /// <exception cref="SocketException" />
  56. public TcpClient(IPEndPoint endpoint, int timeout)
  57. {
  58. if (endpoint == null) throw new ArgumentNullException(nameof(endpoint));
  59. if (timeout < 1) throw new ArgumentOutOfRangeException(nameof(timeout));
  60. _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  61. if (timeout > 0)
  62. {
  63. RuntimeUtility.InBackground(timeout, () =>
  64. {
  65. if (!_socket.Connected)
  66. {
  67. _socket.Close();
  68. #if !NET20
  69. _socket.Dispose();
  70. #endif
  71. }
  72. }, true);
  73. }
  74. try
  75. {
  76. _socket.Connect(endpoint);
  77. _socket.SendTimeout = 5000;
  78. LocalEndPoint = _socket.LocalEndPoint as IPEndPoint;
  79. RemoteEndPoint = endpoint;
  80. }
  81. catch (SocketException ex)
  82. {
  83. if (ex.SocketErrorCode == SocketError.NotSocket)
  84. {
  85. throw new SocketException((int)SocketError.TimedOut);
  86. }
  87. throw new Exception($"{ex.ErrorCode} {ex.SocketErrorCode}: {ex.Message}");
  88. }
  89. catch
  90. {
  91. throw;
  92. }
  93. }
  94. /// <summary>关闭连接,释放系统资源。</summary>
  95. public void Dispose()
  96. {
  97. try { _socket.Disconnect(false); } catch { }
  98. try { _socket.Close(100); } catch { }
  99. #if !NET20
  100. try { _socket.Dispose(); } catch { }
  101. #endif
  102. }
  103. /// <summary>接收。</summary>
  104. /// <exception cref="ArgumentNullException"></exception>
  105. /// <exception cref="ArgumentOutOfRangeException"></exception>
  106. /// <exception cref="SocketException"></exception>
  107. /// <exception cref="ObjectDisposedException"></exception>
  108. /// <exception cref="System.Security.SecurityException"></exception>
  109. public byte[] Receive(int maxLength = 1024) => _socket.Receive(maxLength);
  110. /// <summary>发送。</summary>
  111. /// <exception cref="ArgumentNullException" />
  112. /// <exception cref="SocketException" />
  113. /// <exception cref="ObjectDisposedException" />
  114. public void Send(byte[] data) => _socket.Send(data);
  115. }
  116. }