diff --git a/doc/api/net.markdown b/doc/api/net.markdown index dab1df76aa..e5056338ef 100644 --- a/doc/api/net.markdown +++ b/doc/api/net.markdown @@ -174,6 +174,25 @@ The `callback` paramenter will be added as an listener for the 'connect' event. +#### socket.bufferSize + +`net.Socket` has the property that `socket.write()` always works. This is to +help users get up an running quickly. The computer cannot necessarily keep up +with the amount of data that is written to a socket - the network connection simply +might be too slow. Node will internally queue up the data written to a socket and +send it out over the wire when it is possible. (Internally it is polling on +the socket's file descriptor for being writable). + +The consequence of this internal buffering is that memory may grow. This +property shows the number of characters currently buffered to be written. +(Number of characters is approximately equal to the number of bytes to be +written, but the buffer may contain strings, and the strings are lazily +encoded, so the exact number of bytes is not known.) + +Users who experience large or growing `bufferSize` should attempt to +"throttle" the data flows in their program with `pause()` and resume()`. + + #### socket.setEncoding(encoding=null) Sets the encoding (either `'ascii'`, `'utf8'`, or `'base64'`) for data that is diff --git a/lib/net.js b/lib/net.js index abfc813c2c..38ef6d616a 100644 --- a/lib/net.js +++ b/lib/net.js @@ -179,6 +179,8 @@ function initSocket(self) { self._writeQueueEncoding = []; self._writeQueueFD = []; self._writeQueueCallbacks = []; + // Number of charactes (which approx. equals number of bytes) + self.bufferSize = 0; self._writeWatcher = ioWatchers.alloc(); self._writeWatcher.socket = self; @@ -192,6 +194,7 @@ function Socket(options) { if (!(this instanceof Socket)) return new Socket(arguments[0], arguments[1]); stream.Stream.call(this); + this.bufferSize = 0; this.fd = null; this.type = null; this.allowHalfOpen = false; @@ -270,6 +273,8 @@ Object.defineProperty(Socket.prototype, 'readyState', { Socket.prototype.write = function(data /* [encoding], [fd], [cb] */) { var encoding, fd, cb; + assert(this.bufferSize >= 0); + // parse arguments if (typeof arguments[1] == 'string') { encoding = arguments[1]; @@ -296,6 +301,7 @@ Socket.prototype.write = function(data /* [encoding], [fd], [cb] */) { if (this._connecting || (this._writeQueue && this._writeQueue.length)) { if (!this._writeQueue) { + this.bufferSize = 0; this._writeQueue = []; this._writeQueueEncoding = []; this._writeQueueFD = []; @@ -309,6 +315,8 @@ Socket.prototype.write = function(data /* [encoding], [fd], [cb] */) { var last = this._writeQueue.length - 1; + this.bufferSize += data.length; + if (typeof data == 'string' && this._writeQueue.length && typeof this._writeQueue[last] === 'string' && @@ -403,6 +411,8 @@ Socket.prototype._writeOut = function(data, encoding, fd, cb) { // (data.length - charsWritten) + // ' bytes into the pool\n'); // Unshift whatever didn't fit onto the buffer + assert(data.length > charsWritten); + this.bufferSize += data.length - charsWritten; this._writeQueue.unshift(data.slice(charsWritten)); this._writeQueueEncoding.unshift(encoding); this._writeQueueCallbacks.unshift(cb); @@ -451,6 +461,7 @@ Socket.prototype._writeOut = function(data, encoding, fd, cb) { //if (!this._writeQueue) initWriteSocket(this); // data should be the next thing to write. + this.bufferSize += leftOver.length; this._writeQueue.unshift(leftOver); this._writeQueueEncoding.unshift(null); this._writeQueueCallbacks.unshift(cb); @@ -478,6 +489,9 @@ Socket.prototype.flush = function() { return true; } + // Only decrement if it's not the END_OF_FILE object... + this.bufferSize -= data.length; + var flushed = this._writeOut(data, encoding, fd, cb); if (!flushed) return false; } @@ -731,7 +745,12 @@ Socket.prototype.destroy = function(exception) { // TODO would like to set _writeQueue to null to avoid extra object alloc, // but lots of code assumes this._writeQueue is always an array. + assert(this.bufferSize >= 0); this._writeQueue = []; + this._writeQueueEncoding = []; + this._writeQueueCallbacks = []; + this._writeQueueFD = []; + this.bufferSize = 0; this.readable = this.writable = false; diff --git a/test/pummel/test-net-throttle.js b/test/pummel/test-net-throttle.js index e1e0eb8a30..c53a592aee 100644 --- a/test/pummel/test-net-throttle.js +++ b/test/pummel/test-net-throttle.js @@ -2,7 +2,7 @@ var common = require('../common'); var assert = require('assert'); var net = require('net'); -var N = 160 * 1024; // 30kb +var N = 160 * 1024; var chars_recved = 0; var npauses = 0; @@ -17,6 +17,9 @@ console.log('start server on port ' + common.PORT); var server = net.createServer(function(connection) { connection.addListener('connect', function() { assert.equal(false, connection.write(body)); + console.log('bufferSize: ' + connection.bufferSize); + assert.ok(0 <= connection.bufferSize && + connection.bufferSize <= N); connection.end(); }); }); diff --git a/test/simple/test-net-pingpong.js b/test/simple/test-net-pingpong.js index c154e6783c..da4be51c3c 100644 --- a/test/simple/test-net-pingpong.js +++ b/test/simple/test-net-pingpong.js @@ -21,6 +21,11 @@ function pingPongTest(port, host) { socket.setEncoding('utf8'); socket.addListener('data', function(data) { + // Since we never queue data (we're always waiting for the PING + // before sending a pong) the writeQueueSize should always be less + // than one message. + assert.ok(0 <= socket.bufferSize && socket.bufferSize <= 4); + console.log('server got: ' + data); assert.equal(true, socket.writable); assert.equal(true, socket.readable);