tcp-client.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. Copyright 2012 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. Author: Boris Smus (smus@chromium.org)
  13. */
  14. (function(exports) {
  15. // Define some local variables here.
  16. var socket = chrome.socket || chrome.experimental.socket;
  17. var dns = chrome.experimental.dns;
  18. /**
  19. * Creates an instance of the client
  20. *
  21. * @param {String} host The remote host to connect to
  22. * @param {Number} port The port to connect to at the remote host
  23. */
  24. function TcpClient(host, port) {
  25. this.host = host;
  26. this.port = port;
  27. // Callback functions.
  28. this.callbacks = {
  29. connect: null, // Called when socket is connected.
  30. disconnect: null, // Called when socket is disconnected.
  31. recv: null, // Called when client receives data from server.
  32. sent: null // Called when client sends data to server.
  33. };
  34. // Socket.
  35. this.socketId = null;
  36. this.isConnected = false;
  37. log('initialized tcp client');
  38. }
  39. /**
  40. * Connects to the TCP socket, and creates an open socket.
  41. *
  42. * @see http://developer.chrome.com/trunk/apps/socket.html#method-create
  43. * @param {Function} callback The function to call on connection
  44. */
  45. TcpClient.prototype.connect = function(callback) {
  46. // First resolve the hostname to an IP.
  47. dns.resolve(this.host, function(result) {
  48. this.addr = result.address;
  49. socket.create('tcp', {}, this._onCreate.bind(this));
  50. // Register connect callback.
  51. this.callbacks.connect = callback;
  52. }.bind(this));
  53. };
  54. /**
  55. * Sends a message down the wire to the remote side
  56. *
  57. * @see http://developer.chrome.com/trunk/apps/socket.html#method-write
  58. * @param {String} msg The message to send
  59. * @param {Function} callback The function to call when the message has sent
  60. */
  61. TcpClient.prototype.sendMessage = function(msg, callback) {
  62. this._stringToArrayBuffer(msg + '\n', function(arrayBuffer) {
  63. socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
  64. }.bind(this));
  65. // Register sent callback.
  66. this.callbacks.sent = callback;
  67. };
  68. /**
  69. * Sets the callback for when a message is received
  70. *
  71. * @param {Function} callback The function to call when a message has arrived
  72. */
  73. TcpClient.prototype.addResponseListener = function(callback) {
  74. // Register received callback.
  75. this.callbacks.recv = callback;
  76. };
  77. /**
  78. * Disconnects from the remote side
  79. *
  80. * @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect
  81. */
  82. TcpClient.prototype.disconnect = function() {
  83. socket.disconnect(this.socketId);
  84. this.isConnected = false;
  85. };
  86. /**
  87. * The callback function used for when we attempt to have Chrome
  88. * create a socket. If the socket is successfully created
  89. * we go ahead and connect to the remote side.
  90. *
  91. * @private
  92. * @see http://developer.chrome.com/trunk/apps/socket.html#method-connect
  93. * @param {Object} createInfo The socket details
  94. */
  95. TcpClient.prototype._onCreate = function(createInfo) {
  96. this.socketId = createInfo.socketId;
  97. if (this.socketId > 0) {
  98. socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this));
  99. this.isConnected = true;
  100. } else {
  101. error('Unable to create socket');
  102. }
  103. };
  104. /**
  105. * The callback function used for when we attempt to have Chrome
  106. * connect to the remote side. If a successful connection is
  107. * made then polling starts to check for data to read
  108. *
  109. * @private
  110. * @param {Number} resultCode Indicates whether the connection was successful
  111. */
  112. TcpClient.prototype._onConnectComplete = function(resultCode) {
  113. // Start polling for reads.
  114. setInterval(this._periodicallyRead.bind(this), 500);
  115. if (this.callbacks.connect) {
  116. console.log('connect complete');
  117. this.callbacks.connect();
  118. }
  119. log('onConnectComplete');
  120. };
  121. /**
  122. * Checks for new data to read from the socket
  123. *
  124. * @see http://developer.chrome.com/trunk/apps/socket.html#method-read
  125. */
  126. TcpClient.prototype._periodicallyRead = function() {
  127. socket.read(this.socketId, null, this._onDataRead.bind(this));
  128. };
  129. /**
  130. * Callback function for when data has been read from the socket.
  131. * Converts the array buffer that is read in to a string
  132. * and sends it on for further processing by passing it to
  133. * the previously assigned callback function.
  134. *
  135. * @private
  136. * @see TcpClient.prototype.addResponseListener
  137. * @param {Object} readInfo The incoming message
  138. */
  139. TcpClient.prototype._onDataRead = function(readInfo) {
  140. // Call received callback if there's data in the response.
  141. if (readInfo.resultCode > 0 && this.callbacks.recv) {
  142. log('onDataRead');
  143. // Convert ArrayBuffer to string.
  144. this._arrayBufferToString(readInfo.data, function(str) {
  145. this.callbacks.recv(str);
  146. }.bind(this));
  147. }
  148. };
  149. /**
  150. * Callback for when data has been successfully
  151. * written to the socket.
  152. *
  153. * @private
  154. * @param {Object} writeInfo The outgoing message
  155. */
  156. TcpClient.prototype._onWriteComplete = function(writeInfo) {
  157. log('onWriteComplete');
  158. // Call sent callback.
  159. if (this.callbacks.sent) {
  160. this.callbacks.sent(writeInfo);
  161. }
  162. };
  163. /**
  164. * Converts an array buffer to a string
  165. *
  166. * @private
  167. * @param {ArrayBuffer} buf The buffer to convert
  168. * @param {Function} callback The function to call when conversion is complete
  169. */
  170. TcpClient.prototype._arrayBufferToString = function(buf, callback) {
  171. var bb = new Blob([new Uint8Array(buf)]);
  172. var f = new FileReader();
  173. f.onload = function(e) {
  174. callback(e.target.result);
  175. };
  176. f.readAsText(bb);
  177. };
  178. /**
  179. * Converts a string to an array buffer
  180. *
  181. * @private
  182. * @param {String} str The string to convert
  183. * @param {Function} callback The function to call when conversion is complete
  184. */
  185. TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
  186. var bb = new Blob([str]);
  187. var f = new FileReader();
  188. f.onload = function(e) {
  189. callback(e.target.result);
  190. };
  191. f.readAsArrayBuffer(bb);
  192. };
  193. /**
  194. * Wrapper function for logging
  195. */
  196. function log(msg) {
  197. console.log(msg);
  198. }
  199. /**
  200. * Wrapper function for error logging
  201. */
  202. function error(msg) {
  203. console.error(msg);
  204. }
  205. exports.TcpClient = TcpClient;
  206. })(window);