|
@@ -23,12 +23,13 @@
|
|
|
#include "datagramUDPHeader.h"
|
|
#include "datagramUDPHeader.h"
|
|
|
#include "pprerror.h"
|
|
#include "pprerror.h"
|
|
|
#include "config_net.h"
|
|
#include "config_net.h"
|
|
|
|
|
+#include "config_express.h" // for collect_tcp
|
|
|
|
|
+#include "clockObject.h"
|
|
|
|
|
|
|
|
-#include <prerror.h>
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::Constructor
|
|
// Function: Connection::Constructor
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Creates a connection. Normally this constructor
|
|
// Description: Creates a connection. Normally this constructor
|
|
|
// should not be used directly by user code; use one of
|
|
// should not be used directly by user code; use one of
|
|
|
// the methods in ConnectionManager to make a new
|
|
// the methods in ConnectionManager to make a new
|
|
@@ -40,11 +41,15 @@ Connection(ConnectionManager *manager, PRFileDesc *socket) :
|
|
|
_socket(socket)
|
|
_socket(socket)
|
|
|
{
|
|
{
|
|
|
_write_mutex = PR_NewLock();
|
|
_write_mutex = PR_NewLock();
|
|
|
|
|
+ _collect_tcp = collect_tcp;
|
|
|
|
|
+ _collect_tcp_interval = collect_tcp_interval;
|
|
|
|
|
+ _queued_data_start = 0.0;
|
|
|
|
|
+ _queued_count = 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::Destructor
|
|
// Function: Connection::Destructor
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Closes a connection.
|
|
// Description: Closes a connection.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
Connection::
|
|
Connection::
|
|
@@ -53,6 +58,8 @@ Connection::
|
|
|
<< "Deleting connection " << (void *)this << "\n";
|
|
<< "Deleting connection " << (void *)this << "\n";
|
|
|
|
|
|
|
|
if (_socket != (PRFileDesc *)NULL) {
|
|
if (_socket != (PRFileDesc *)NULL) {
|
|
|
|
|
+ flush();
|
|
|
|
|
+
|
|
|
PRStatus result = PR_Close(_socket);
|
|
PRStatus result = PR_Close(_socket);
|
|
|
if (result != PR_SUCCESS) {
|
|
if (result != PR_SUCCESS) {
|
|
|
pprerror("PR_Close");
|
|
pprerror("PR_Close");
|
|
@@ -64,7 +71,7 @@ Connection::
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::get_address
|
|
// Function: Connection::get_address
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Returns the address bound to this connection, if it
|
|
// Description: Returns the address bound to this connection, if it
|
|
|
// is a TCP connection.
|
|
// is a TCP connection.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -80,7 +87,7 @@ get_address() const {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::get_manager
|
|
// Function: Connection::get_manager
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Returns a pointer to the ConnectionManager object
|
|
// Description: Returns a pointer to the ConnectionManager object
|
|
|
// that serves this connection.
|
|
// that serves this connection.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -91,7 +98,7 @@ get_manager() const {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::get_socket
|
|
// Function: Connection::get_socket
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Returns the internal NSPR pointer that defines the
|
|
// Description: Returns the internal NSPR pointer that defines the
|
|
|
// connection.
|
|
// connection.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -100,9 +107,107 @@ get_socket() const {
|
|
|
return _socket;
|
|
return _socket;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::set_collect_tcp
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Enables or disables "collect-tcp" mode. In this
|
|
|
|
|
+// mode, individual TCP packets are not sent
|
|
|
|
|
+// immediately, but rather they are collected together
|
|
|
|
|
+// and accumulated to be sent periodically as one larger
|
|
|
|
|
+// TCP packet. This cuts down on overhead from the
|
|
|
|
|
+// TCP/IP protocol, especially if many small packets
|
|
|
|
|
+// need to be sent on the same connection, but it
|
|
|
|
|
+// introduces additional latency (since packets must be
|
|
|
|
|
+// held before they can be sent).
|
|
|
|
|
+//
|
|
|
|
|
+// See set_collect_tcp_interval() to specify the
|
|
|
|
|
+// interval of time for which to hold packets before
|
|
|
|
|
+// sending them.
|
|
|
|
|
+//
|
|
|
|
|
+// If you enable this mode, you may also need to
|
|
|
|
|
+// periodically call consider_flush() to flush the queue
|
|
|
|
|
+// if no packets have been sent recently.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Connection::
|
|
|
|
|
+set_collect_tcp(bool collect_tcp) {
|
|
|
|
|
+ _collect_tcp = collect_tcp;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::get_collect_tcp
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Returns the current setting of "collect-tcp" mode.
|
|
|
|
|
+// See set_collect_tcp().
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+bool Connection::
|
|
|
|
|
+get_collect_tcp() const {
|
|
|
|
|
+ return _collect_tcp;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::set_collect_tcp_interval
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Specifies the interval in time, in seconds, for which
|
|
|
|
|
+// to hold TCP packets before sending all of the
|
|
|
|
|
+// recently received packets at once. This only has
|
|
|
|
|
+// meaning if "collect-tcp" mode is enabled; see
|
|
|
|
|
+// set_collect_tcp().
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Connection::
|
|
|
|
|
+set_collect_tcp_interval(double interval) {
|
|
|
|
|
+ _collect_tcp_interval = interval;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::get_collect_tcp_interval
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Returns the interval in time, in seconds, for which
|
|
|
|
|
+// to hold TCP packets before sending all of the
|
|
|
|
|
+// recently received packets at once. This only has
|
|
|
|
|
+// meaning if "collect-tcp" mode is enabled; see
|
|
|
|
|
+// set_collect_tcp().
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+double Connection::
|
|
|
|
|
+get_collect_tcp_interval() const {
|
|
|
|
|
+ return _collect_tcp_interval;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::consider_flush
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Sends the most recently queued TCP datagram(s) if
|
|
|
|
|
+// enough time has elapsed. This only has meaning if
|
|
|
|
|
+// set_collect_tcp() has been set to true.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+bool Connection::
|
|
|
|
|
+consider_flush() {
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
|
|
+
|
|
|
|
|
+ if (!_collect_tcp ||
|
|
|
|
|
+ ClockObject::get_global_clock()->get_real_time() - _queued_data_start >= _collect_tcp_interval) {
|
|
|
|
|
+ return do_flush();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::flush
|
|
|
|
|
+// Access: Published
|
|
|
|
|
+// Description: Sends the most recently queued TCP datagram(s) now.
|
|
|
|
|
+// This only has meaning if set_collect_tcp() has been
|
|
|
|
|
+// set to true.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+bool Connection::
|
|
|
|
|
+flush() {
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
|
|
+ return do_flush();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_nonblock
|
|
// Function: Connection::set_nonblock
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets whether nonblocking I/O should be in effect.
|
|
// Description: Sets whether nonblocking I/O should be in effect.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -115,7 +220,7 @@ set_nonblock(bool flag) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_linger
|
|
// Function: Connection::set_linger
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets the time to linger on close if data is present.
|
|
// Description: Sets the time to linger on close if data is present.
|
|
|
// If flag is false, when you close a socket with data
|
|
// If flag is false, when you close a socket with data
|
|
|
// available the system attempts to deliver the data to
|
|
// available the system attempts to deliver the data to
|
|
@@ -136,7 +241,7 @@ set_linger(bool flag, double time) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_reuse_addr
|
|
// Function: Connection::set_reuse_addr
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets whether local address reuse is allowed.
|
|
// Description: Sets whether local address reuse is allowed.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -149,7 +254,7 @@ set_reuse_addr(bool flag) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_keep_alive
|
|
// Function: Connection::set_keep_alive
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets whether the connection is periodically tested to
|
|
// Description: Sets whether the connection is periodically tested to
|
|
|
// see if it is still alive.
|
|
// see if it is still alive.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -163,7 +268,7 @@ set_keep_alive(bool flag) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_recv_buffer_size
|
|
// Function: Connection::set_recv_buffer_size
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets the size of the receive buffer, in bytes.
|
|
// Description: Sets the size of the receive buffer, in bytes.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -176,7 +281,7 @@ set_recv_buffer_size(int size) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_send_buffer_size
|
|
// Function: Connection::set_send_buffer_size
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets the size of the send buffer, in bytes.
|
|
// Description: Sets the size of the send buffer, in bytes.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -189,7 +294,7 @@ set_send_buffer_size(int size) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_ip_time_to_live
|
|
// Function: Connection::set_ip_time_to_live
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets IP time-to-live.
|
|
// Description: Sets IP time-to-live.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -202,7 +307,7 @@ set_ip_time_to_live(int ttl) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_ip_type_of_service
|
|
// Function: Connection::set_ip_type_of_service
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets IP type-of-service and precedence.
|
|
// Description: Sets IP type-of-service and precedence.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -215,7 +320,7 @@ set_ip_type_of_service(int tos) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_no_delay
|
|
// Function: Connection::set_no_delay
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: If flag is true, this disables the Nagle algorithm,
|
|
// Description: If flag is true, this disables the Nagle algorithm,
|
|
|
// and prevents delaying of send to coalesce packets.
|
|
// and prevents delaying of send to coalesce packets.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
@@ -229,7 +334,7 @@ set_no_delay(bool flag) {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: Connection::set_max_segment
|
|
// Function: Connection::set_max_segment
|
|
|
-// Access: Public
|
|
|
|
|
|
|
+// Access: Published
|
|
|
// Description: Sets the maximum segment size.
|
|
// Description: Sets the maximum segment size.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void Connection::
|
|
void Connection::
|
|
@@ -253,65 +358,49 @@ bool Connection::
|
|
|
send_datagram(const NetDatagram &datagram) {
|
|
send_datagram(const NetDatagram &datagram) {
|
|
|
nassertr(_socket != (PRFileDesc *)NULL, false);
|
|
nassertr(_socket != (PRFileDesc *)NULL, false);
|
|
|
|
|
|
|
|
- PR_Lock(_write_mutex);
|
|
|
|
|
-
|
|
|
|
|
- PRInt32 bytes_sent;
|
|
|
|
|
- PRInt32 result;
|
|
|
|
|
if (PR_GetDescType(_socket) == PR_DESC_SOCKET_UDP) {
|
|
if (PR_GetDescType(_socket) == PR_DESC_SOCKET_UDP) {
|
|
|
|
|
+ // We have to send UDP right away.
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
DatagramUDPHeader header(datagram);
|
|
DatagramUDPHeader header(datagram);
|
|
|
- string data = header.get_header() + datagram.get_message();
|
|
|
|
|
- bytes_sent = data.length();
|
|
|
|
|
|
|
+ string data;
|
|
|
|
|
+ data += header.get_header();
|
|
|
|
|
+ data += datagram.get_message();
|
|
|
|
|
+
|
|
|
|
|
+ PRInt32 bytes_to_send = data.length();
|
|
|
|
|
+ PRInt32 result;
|
|
|
result = PR_SendTo(_socket,
|
|
result = PR_SendTo(_socket,
|
|
|
- data.data(), bytes_sent,
|
|
|
|
|
|
|
+ data.data(), bytes_to_send,
|
|
|
0,
|
|
0,
|
|
|
datagram.get_address().get_addr(),
|
|
datagram.get_address().get_addr(),
|
|
|
PR_INTERVAL_NO_TIMEOUT);
|
|
PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
|
+ PRErrorCode errcode = PR_GetError();
|
|
|
|
|
|
|
|
if (net_cat.is_debug()) {
|
|
if (net_cat.is_debug()) {
|
|
|
header.verify_datagram(datagram);
|
|
header.verify_datagram(datagram);
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- DatagramTCPHeader header(datagram);
|
|
|
|
|
- string data = header.get_header() + datagram.get_message();
|
|
|
|
|
- bytes_sent = data.length();
|
|
|
|
|
- result = PR_Send(_socket,
|
|
|
|
|
- data.data(), bytes_sent,
|
|
|
|
|
- 0,
|
|
|
|
|
- PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
|
|
|
|
|
|
- if (net_cat.is_debug()) {
|
|
|
|
|
- header.verify_datagram(datagram);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
|
|
+ return check_send_error(result, errcode, bytes_to_send);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- PRErrorCode errcode = PR_GetError();
|
|
|
|
|
-
|
|
|
|
|
- PR_Unlock(_write_mutex);
|
|
|
|
|
-
|
|
|
|
|
- if (result < 0) {
|
|
|
|
|
- if (errcode == PR_CONNECT_RESET_ERROR
|
|
|
|
|
-#ifdef PR_SOCKET_SHUTDOWN_ERROR
|
|
|
|
|
- || errcode == PR_SOCKET_SHUTDOWN_ERROR
|
|
|
|
|
- || errcode == PR_CONNECT_ABORTED_ERROR
|
|
|
|
|
-#endif
|
|
|
|
|
- ) {
|
|
|
|
|
- // The connection has been reset; tell our manager about it
|
|
|
|
|
- // and ignore it.
|
|
|
|
|
- if (_manager != (ConnectionManager *)NULL) {
|
|
|
|
|
- _manager->connection_reset(this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- } else if (errcode != PR_PENDING_INTERRUPT_ERROR) {
|
|
|
|
|
- pprerror("PR_SendTo");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // We might queue up TCP packets for later sending.
|
|
|
|
|
+ DatagramTCPHeader header(datagram);
|
|
|
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
|
|
+ _queued_data += header.get_header();
|
|
|
|
|
+ _queued_data += datagram.get_message();
|
|
|
|
|
+ _queued_count++;
|
|
|
|
|
+
|
|
|
|
|
+ if (net_cat.is_debug()) {
|
|
|
|
|
+ header.verify_datagram(datagram);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- } else if (result != bytes_sent) {
|
|
|
|
|
- net_cat.error() << "Not enough bytes sent to socket.\n";
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ if (!_collect_tcp ||
|
|
|
|
|
+ ClockObject::get_global_clock()->get_real_time() - _queued_data_start >= _collect_tcp_interval) {
|
|
|
|
|
+ return do_flush();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -326,31 +415,93 @@ bool Connection::
|
|
|
send_raw_datagram(const NetDatagram &datagram) {
|
|
send_raw_datagram(const NetDatagram &datagram) {
|
|
|
nassertr(_socket != (PRFileDesc *)NULL, false);
|
|
nassertr(_socket != (PRFileDesc *)NULL, false);
|
|
|
|
|
|
|
|
- PR_Lock(_write_mutex);
|
|
|
|
|
-
|
|
|
|
|
- PRInt32 bytes_sent;
|
|
|
|
|
- PRInt32 result;
|
|
|
|
|
if (PR_GetDescType(_socket) == PR_DESC_SOCKET_UDP) {
|
|
if (PR_GetDescType(_socket) == PR_DESC_SOCKET_UDP) {
|
|
|
|
|
+ // We have to send UDP right away.
|
|
|
|
|
+
|
|
|
string data = datagram.get_message();
|
|
string data = datagram.get_message();
|
|
|
- bytes_sent = data.length();
|
|
|
|
|
|
|
+ PRInt32 bytes_to_send = data.length();
|
|
|
|
|
+
|
|
|
|
|
+ if (net_cat.is_spam()) {
|
|
|
|
|
+ net_cat.spam()
|
|
|
|
|
+ << "Sending UDP datagram with "
|
|
|
|
|
+ << bytes_to_send << " bytes to " << (void *)this << "\n";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
|
|
+ PRInt32 result;
|
|
|
result = PR_SendTo(_socket,
|
|
result = PR_SendTo(_socket,
|
|
|
- data.data(), bytes_sent,
|
|
|
|
|
|
|
+ data.data(), bytes_to_send,
|
|
|
0,
|
|
0,
|
|
|
datagram.get_address().get_addr(),
|
|
datagram.get_address().get_addr(),
|
|
|
PR_INTERVAL_NO_TIMEOUT);
|
|
PR_INTERVAL_NO_TIMEOUT);
|
|
|
- } else {
|
|
|
|
|
- string data = datagram.get_message();
|
|
|
|
|
- bytes_sent = data.length();
|
|
|
|
|
- result = PR_Send(_socket,
|
|
|
|
|
- data.data(), bytes_sent,
|
|
|
|
|
- 0,
|
|
|
|
|
- PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
|
|
|
+ PRErrorCode errcode = PR_GetError();
|
|
|
|
|
+
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
|
|
+ return check_send_error(result, errcode, bytes_to_send);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We might queue up TCP packets for later sending.
|
|
|
|
|
+
|
|
|
|
|
+ PR_Lock(_write_mutex);
|
|
|
|
|
+ _queued_data += datagram.get_message();
|
|
|
|
|
+ _queued_count++;
|
|
|
|
|
+
|
|
|
|
|
+ if (!_collect_tcp ||
|
|
|
|
|
+ ClockObject::get_global_clock()->get_real_time() - _queued_data_start >= _collect_tcp_interval) {
|
|
|
|
|
+ return do_flush();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::do_flush
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: The private implementation of flush(), this assumes
|
|
|
|
|
+// the _write_mutex has already been locked on entry.
|
|
|
|
|
+// It will be unlocked on return.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+bool Connection::
|
|
|
|
|
+do_flush() {
|
|
|
|
|
+ PRInt32 bytes_to_send = _queued_data.length();
|
|
|
|
|
+ if (bytes_to_send == 0) {
|
|
|
|
|
+ _queued_count = 0;
|
|
|
|
|
+ _queued_data_start = ClockObject::get_global_clock()->get_real_time();
|
|
|
|
|
+ PR_Unlock(_write_mutex);
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (net_cat.is_spam()) {
|
|
|
|
|
+ net_cat.spam()
|
|
|
|
|
+ << "Sending " << _queued_count << " TCP datagram(s) with "
|
|
|
|
|
+ << bytes_to_send << " total bytes to " << (void *)this << "\n";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PRInt32 result;
|
|
|
|
|
+ result = PR_Send(_socket,
|
|
|
|
|
+ _queued_data.data(), bytes_to_send,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ PR_INTERVAL_NO_TIMEOUT);
|
|
|
PRErrorCode errcode = PR_GetError();
|
|
PRErrorCode errcode = PR_GetError();
|
|
|
|
|
|
|
|
|
|
+ _queued_data = string();
|
|
|
|
|
+ _queued_count = 0;
|
|
|
|
|
+ _queued_data_start = ClockObject::get_global_clock()->get_real_time();
|
|
|
|
|
+
|
|
|
PR_Unlock(_write_mutex);
|
|
PR_Unlock(_write_mutex);
|
|
|
|
|
|
|
|
|
|
+ return check_send_error(result, errcode, bytes_to_send);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Connection::check_send_error
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Checks the return value of a PR_Send() or PR_SendTo()
|
|
|
|
|
+// call.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+bool Connection::
|
|
|
|
|
+check_send_error(PRInt32 result, PRErrorCode errcode, PRInt32 bytes_to_send) {
|
|
|
if (result < 0) {
|
|
if (result < 0) {
|
|
|
if (errcode == PR_CONNECT_RESET_ERROR
|
|
if (errcode == PR_CONNECT_RESET_ERROR
|
|
|
#ifdef PR_SOCKET_SHUTDOWN_ERROR
|
|
#ifdef PR_SOCKET_SHUTDOWN_ERROR
|
|
@@ -370,7 +521,7 @@ send_raw_datagram(const NetDatagram &datagram) {
|
|
|
|
|
|
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
- } else if (result != bytes_sent) {
|
|
|
|
|
|
|
+ } else if (result != bytes_to_send) {
|
|
|
net_cat.error() << "Not enough bytes sent to socket.\n";
|
|
net_cat.error() << "Not enough bytes sent to socket.\n";
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|