KcpClientConnection.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System.Net;
  2. using System.Net.Sockets;
  3. namespace kcp2k
  4. {
  5. public class KcpClientConnection : KcpConnection
  6. {
  7. // IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even
  8. // if MaxMessageSize is larger. kcp always sends in MTU
  9. // segments and having a buffer smaller than MTU would
  10. // silently drop excess data.
  11. // => we need the MTU to fit channel + message!
  12. readonly byte[] rawReceiveBuffer = new byte[Kcp.MTU_DEF];
  13. // helper function to resolve host to IPAddress
  14. public static bool ResolveHostname(string hostname, out IPAddress[] addresses)
  15. {
  16. try
  17. {
  18. addresses = Dns.GetHostAddresses(hostname);
  19. return addresses.Length >= 1;
  20. }
  21. catch (SocketException)
  22. {
  23. Log.Info($"Failed to resolve host: {hostname}");
  24. addresses = null;
  25. return false;
  26. }
  27. }
  28. // EndPoint & Receive functions can be overwritten for where-allocation:
  29. // https://github.com/vis2k/where-allocation
  30. // NOTE: Client's SendTo doesn't allocate, don't need a virtual.
  31. protected virtual void CreateRemoteEndPoint(IPAddress[] addresses, ushort port) =>
  32. remoteEndPoint = new IPEndPoint(addresses[0], port);
  33. protected virtual int ReceiveFrom(byte[] buffer) =>
  34. socket.ReceiveFrom(buffer, ref remoteEndPoint);
  35. public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
  36. {
  37. Log.Info($"KcpClient: connect to {host}:{port}");
  38. // try resolve host name
  39. if (ResolveHostname(host, out IPAddress[] addresses))
  40. {
  41. // create remote endpoint
  42. CreateRemoteEndPoint(addresses, port);
  43. // create socket
  44. socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
  45. socket.Connect(remoteEndPoint);
  46. // set up kcp
  47. SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
  48. // client should send handshake to server as very first message
  49. SendHandshake();
  50. RawReceive();
  51. }
  52. // otherwise call OnDisconnected to let the user know.
  53. else OnDisconnected();
  54. }
  55. // call from transport update
  56. public void RawReceive()
  57. {
  58. try
  59. {
  60. if (socket != null)
  61. {
  62. while (socket.Poll(0, SelectMode.SelectRead))
  63. {
  64. int msgLength = ReceiveFrom(rawReceiveBuffer);
  65. // IMPORTANT: detect if buffer was too small for the
  66. // received msgLength. otherwise the excess
  67. // data would be silently lost.
  68. // (see ReceiveFrom documentation)
  69. if (msgLength <= rawReceiveBuffer.Length)
  70. {
  71. //Log.Debug($"KCP: client raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}");
  72. RawInput(rawReceiveBuffer, msgLength);
  73. }
  74. else
  75. {
  76. Log.Error($"KCP ClientConnection: message of size {msgLength} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting.");
  77. Disconnect();
  78. }
  79. }
  80. }
  81. }
  82. // this is fine, the socket might have been closed in the other end
  83. catch (SocketException) {}
  84. }
  85. protected override void Dispose()
  86. {
  87. socket.Close();
  88. socket = null;
  89. }
  90. protected override void RawSend(byte[] data, int length)
  91. {
  92. socket.Send(data, length, SocketFlags.None);
  93. }
  94. }
  95. }