TcpClient.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // TcpClient.cs
  2. //
  3. // Author:
  4. // Phillip Pearson ([email protected])
  5. // Gonzalo Paniagua Javier ([email protected])
  6. // Sridhar Kulkarni ([email protected])
  7. // Marek Safar ([email protected])
  8. //
  9. // Copyright (C) 2001, Phillip Pearson http://www.myelin.co.nz
  10. // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
  11. // Copyright 2011 Xamarin Inc.
  12. //
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. using System;
  34. using System.Net;
  35. using System.Threading.Tasks;
  36. namespace System.Net.Sockets
  37. {
  38. public class TcpClient : IDisposable {
  39. enum Properties : uint {
  40. LingerState = 1,
  41. NoDelay = 2,
  42. ReceiveBufferSize = 4,
  43. ReceiveTimeout = 8,
  44. SendBufferSize = 16,
  45. SendTimeout = 32
  46. }
  47. // private data
  48. NetworkStream stream;
  49. bool active;
  50. Socket client;
  51. bool disposed;
  52. Properties values;
  53. int recv_timeout, send_timeout;
  54. int recv_buffer_size, send_buffer_size;
  55. LingerOption linger_state;
  56. bool no_delay;
  57. private void Init (AddressFamily family)
  58. {
  59. active = false;
  60. if(client != null) {
  61. client.Close();
  62. client = null;
  63. }
  64. client = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
  65. }
  66. public TcpClient ()
  67. {
  68. Init(AddressFamily.InterNetwork);
  69. client.Bind(new IPEndPoint(IPAddress.Any, 0));
  70. }
  71. internal TcpClient (Socket s)
  72. {
  73. client = s;
  74. }
  75. public TcpClient (AddressFamily family)
  76. {
  77. if (family != AddressFamily.InterNetwork &&
  78. family != AddressFamily.InterNetworkV6) {
  79. throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
  80. }
  81. Init (family);
  82. IPAddress any = IPAddress.Any;
  83. if (family == AddressFamily.InterNetworkV6)
  84. any = IPAddress.IPv6Any;
  85. client.Bind (new IPEndPoint (any, 0));
  86. }
  87. public TcpClient (IPEndPoint localEP)
  88. {
  89. Init (localEP.AddressFamily);
  90. client.Bind (localEP);
  91. }
  92. public TcpClient (string hostname, int port)
  93. {
  94. Connect(hostname, port);
  95. }
  96. protected bool Active {
  97. get { return active; }
  98. set { active = value; }
  99. }
  100. public Socket Client {
  101. get { return client; }
  102. set {
  103. client = value;
  104. stream = null;
  105. }
  106. }
  107. public int Available {
  108. get { return client.Available; }
  109. }
  110. public bool Connected {
  111. get { return client.Connected; }
  112. }
  113. public bool ExclusiveAddressUse {
  114. get {
  115. return(client.ExclusiveAddressUse);
  116. }
  117. set {
  118. client.ExclusiveAddressUse = value;
  119. }
  120. }
  121. public LingerOption LingerState {
  122. get {
  123. if ((values & Properties.LingerState) != 0)
  124. return linger_state;
  125. return (LingerOption) client.GetSocketOption (SocketOptionLevel.Socket,
  126. SocketOptionName.Linger);
  127. }
  128. set {
  129. if (!client.Connected) {
  130. linger_state = value;
  131. values |= Properties.LingerState;
  132. return;
  133. }
  134. client.SetSocketOption(
  135. SocketOptionLevel.Socket,
  136. SocketOptionName.Linger, value);
  137. }
  138. }
  139. public bool NoDelay {
  140. get {
  141. if ((values & Properties.NoDelay) != 0)
  142. return no_delay;
  143. return (bool)client.GetSocketOption(
  144. SocketOptionLevel.Tcp,
  145. SocketOptionName.NoDelay);
  146. }
  147. set {
  148. if (!client.Connected) {
  149. no_delay = value;
  150. values |= Properties.NoDelay;
  151. return;
  152. }
  153. client.SetSocketOption(
  154. SocketOptionLevel.Tcp,
  155. SocketOptionName.NoDelay, value ? 1 : 0);
  156. }
  157. }
  158. public int ReceiveBufferSize {
  159. get {
  160. if ((values & Properties.ReceiveBufferSize) != 0)
  161. return recv_buffer_size;
  162. return (int)client.GetSocketOption(
  163. SocketOptionLevel.Socket,
  164. SocketOptionName.ReceiveBuffer);
  165. }
  166. set {
  167. if (!client.Connected) {
  168. recv_buffer_size = value;
  169. values |= Properties.ReceiveBufferSize;
  170. return;
  171. }
  172. client.SetSocketOption(
  173. SocketOptionLevel.Socket,
  174. SocketOptionName.ReceiveBuffer, value);
  175. }
  176. }
  177. public int ReceiveTimeout {
  178. get {
  179. if ((values & Properties.ReceiveTimeout) != 0)
  180. return recv_timeout;
  181. return (int)client.GetSocketOption(
  182. SocketOptionLevel.Socket,
  183. SocketOptionName.ReceiveTimeout);
  184. }
  185. set {
  186. if (!client.Connected) {
  187. recv_timeout = value;
  188. values |= Properties.ReceiveTimeout;
  189. return;
  190. }
  191. client.SetSocketOption(
  192. SocketOptionLevel.Socket,
  193. SocketOptionName.ReceiveTimeout, value);
  194. }
  195. }
  196. public int SendBufferSize {
  197. get {
  198. if ((values & Properties.SendBufferSize) != 0)
  199. return send_buffer_size;
  200. return (int)client.GetSocketOption(
  201. SocketOptionLevel.Socket,
  202. SocketOptionName.SendBuffer);
  203. }
  204. set {
  205. if (!client.Connected) {
  206. send_buffer_size = value;
  207. values |= Properties.SendBufferSize;
  208. return;
  209. }
  210. client.SetSocketOption(
  211. SocketOptionLevel.Socket,
  212. SocketOptionName.SendBuffer, value);
  213. }
  214. }
  215. public int SendTimeout {
  216. get {
  217. if ((values & Properties.SendTimeout) != 0)
  218. return send_timeout;
  219. return (int)client.GetSocketOption(
  220. SocketOptionLevel.Socket,
  221. SocketOptionName.SendTimeout);
  222. }
  223. set {
  224. if (!client.Connected) {
  225. send_timeout = value;
  226. values |= Properties.SendTimeout;
  227. return;
  228. }
  229. client.SetSocketOption(
  230. SocketOptionLevel.Socket,
  231. SocketOptionName.SendTimeout, value);
  232. }
  233. }
  234. // methods
  235. public void Close ()
  236. {
  237. Dispose ();
  238. }
  239. public void Connect (IPEndPoint remoteEP)
  240. {
  241. try {
  242. client.Connect (remoteEP);
  243. active = true;
  244. } finally {
  245. CheckDisposed ();
  246. }
  247. }
  248. public void Connect (IPAddress address, int port)
  249. {
  250. Connect(new IPEndPoint(address, port));
  251. }
  252. void SetOptions ()
  253. {
  254. Properties props = values;
  255. values = 0;
  256. if ((props & Properties.LingerState) != 0)
  257. LingerState = linger_state;
  258. if ((props & Properties.NoDelay) != 0)
  259. NoDelay = no_delay;
  260. if ((props & Properties.ReceiveBufferSize) != 0)
  261. ReceiveBufferSize = recv_buffer_size;
  262. if ((props & Properties.ReceiveTimeout) != 0)
  263. ReceiveTimeout = recv_timeout;
  264. if ((props & Properties.SendBufferSize) != 0)
  265. SendBufferSize = send_buffer_size;
  266. if ((props & Properties.SendTimeout) != 0)
  267. SendTimeout = send_timeout;
  268. }
  269. public void Connect (string hostname, int port)
  270. {
  271. IPAddress [] addresses = Dns.GetHostAddresses (hostname);
  272. Connect (addresses, port);
  273. }
  274. public void Connect (IPAddress[] ipAddresses, int port)
  275. {
  276. CheckDisposed ();
  277. if (ipAddresses == null) {
  278. throw new ArgumentNullException ("ipAddresses");
  279. }
  280. for(int i = 0; i < ipAddresses.Length; i++) {
  281. try {
  282. IPAddress address = ipAddresses[i];
  283. if (address.Equals (IPAddress.Any) ||
  284. address.Equals (IPAddress.IPv6Any)) {
  285. throw new SocketException ((int)SocketError.AddressNotAvailable);
  286. }
  287. Init (address.AddressFamily);
  288. if (address.AddressFamily == AddressFamily.InterNetwork) {
  289. client.Bind (new IPEndPoint (IPAddress.Any, 0));
  290. } else if (address.AddressFamily == AddressFamily.InterNetworkV6) {
  291. client.Bind (new IPEndPoint (IPAddress.IPv6Any, 0));
  292. } else {
  293. throw new NotSupportedException ("This method is only valid for sockets in the InterNetwork and InterNetworkV6 families");
  294. }
  295. Connect (new IPEndPoint (address, port));
  296. if (values != 0) {
  297. SetOptions ();
  298. }
  299. break;
  300. } catch (Exception e) {
  301. /* Reinitialise the socket so
  302. * other properties still work
  303. * (see no-arg constructor)
  304. */
  305. Init (AddressFamily.InterNetwork);
  306. /* This is the last known
  307. * address, so re-throw the
  308. * exception
  309. */
  310. if (i == ipAddresses.Length - 1) {
  311. throw e;
  312. }
  313. }
  314. }
  315. }
  316. public void EndConnect (IAsyncResult asyncResult)
  317. {
  318. client.EndConnect (asyncResult);
  319. }
  320. public IAsyncResult BeginConnect (IPAddress address, int port, AsyncCallback requestCallback, object state)
  321. {
  322. return client.BeginConnect (address, port, requestCallback, state);
  323. }
  324. public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
  325. {
  326. return client.BeginConnect (addresses, port, requestCallback, state);
  327. }
  328. public IAsyncResult BeginConnect (string host, int port, AsyncCallback requestCallback, object state)
  329. {
  330. return client.BeginConnect (host, port, requestCallback, state);
  331. }
  332. public void Dispose ()
  333. {
  334. Dispose (true);
  335. GC.SuppressFinalize (this);
  336. }
  337. protected virtual void Dispose (bool disposing)
  338. {
  339. if (disposed)
  340. return;
  341. disposed = true;
  342. if (disposing) {
  343. // release managed resources
  344. NetworkStream s = stream;
  345. stream = null;
  346. if (s != null) {
  347. // This closes the socket as well, as the NetworkStream
  348. // owns the socket.
  349. s.Close();
  350. active = false;
  351. s = null;
  352. } else if (client != null){
  353. client.Close ();
  354. client = null;
  355. }
  356. }
  357. }
  358. ~TcpClient ()
  359. {
  360. Dispose (false);
  361. }
  362. public NetworkStream GetStream()
  363. {
  364. try {
  365. if (stream == null)
  366. stream = new NetworkStream (client, true);
  367. return stream;
  368. }
  369. finally { CheckDisposed (); }
  370. }
  371. public Task ConnectAsync (IPAddress address, int port)
  372. {
  373. return Task.Factory.FromAsync (BeginConnect, EndConnect, address, port, null);
  374. }
  375. public Task ConnectAsync (IPAddress[] addresses, int port)
  376. {
  377. return Task.Factory.FromAsync (BeginConnect, EndConnect, addresses, port, null);
  378. }
  379. public Task ConnectAsync (string host, int port)
  380. {
  381. return Task.Factory.FromAsync (BeginConnect, EndConnect, host, port, null);
  382. }
  383. private void CheckDisposed ()
  384. {
  385. if (disposed)
  386. throw new ObjectDisposedException (GetType().FullName);
  387. }
  388. }
  389. }