summaryrefslogtreecommitdiffstats
path: root/net/socket/tcp_client_socket_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/socket/tcp_client_socket_win.cc')
-rw-r--r--net/socket/tcp_client_socket_win.cc256
1 files changed, 164 insertions, 92 deletions
diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc
index 469fffd..e6dec8f 100644
--- a/net/socket/tcp_client_socket_win.cc
+++ b/net/socket/tcp_client_socket_win.cc
@@ -29,6 +29,109 @@ namespace net {
namespace {
+bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) {
+ int rv = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError();
+ return rv == 0;
+}
+
+bool SetSocketSendBufferSize(SOCKET socket, int32 size) {
+ int rv = setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<const char*>(&size), sizeof(size));
+ DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError();
+ return rv == 0;
+}
+
+// Sets socket parameters. Returns the OS error code (or 0 on
+// success).
+int SetupSocket(SOCKET socket) {
+ // Increase the socket buffer sizes from the default sizes for WinXP. In
+ // performance testing, there is substantial benefit by increasing from 8KB
+ // to 64KB.
+ // See also:
+ // http://support.microsoft.com/kb/823764/EN-US
+ // On Vista, if we manually set these sizes, Vista turns off its receive
+ // window auto-tuning feature.
+ // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx
+ // Since Vista's auto-tune is better than any static value we can could set,
+ // only change these on pre-vista machines.
+ int32 major_version, minor_version, fix_version;
+ base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
+ &fix_version);
+ if (major_version < 6) {
+ const int32 kSocketBufferSize = 64 * 1024;
+ SetSocketReceiveBufferSize(socket, kSocketBufferSize);
+ SetSocketSendBufferSize(socket, kSocketBufferSize);
+ }
+
+ // Disable Nagle.
+ // The Nagle implementation on windows is governed by RFC 896. The idea
+ // behind Nagle is to reduce small packets on the network. When Nagle is
+ // enabled, if a partial packet has been sent, the TCP stack will disallow
+ // further *partial* packets until an ACK has been received from the other
+ // side. Good applications should always strive to send as much data as
+ // possible and avoid partial-packet sends. However, in most real world
+ // applications, there are edge cases where this does not happen, and two
+ // partil packets may be sent back to back. For a browser, it is NEVER
+ // a benefit to delay for an RTT before the second packet is sent.
+ //
+ // As a practical example in Chromium today, consider the case of a small
+ // POST. I have verified this:
+ // Client writes 649 bytes of header (partial packet #1)
+ // Client writes 50 bytes of POST data (partial packet #2)
+ // In the above example, with Nagle, a RTT delay is inserted between these
+ // two sends due to nagle. RTTs can easily be 100ms or more. The best
+ // fix is to make sure that for POSTing data, we write as much data as
+ // possible and minimize partial packets. We will fix that. But disabling
+ // Nagle also ensure we don't run into this delay in other edge cases.
+ // See also:
+ // http://technet.microsoft.com/en-us/library/bb726981.aspx
+ const BOOL kDisableNagle = TRUE;
+ int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<const char*>(&kDisableNagle),
+ sizeof(kDisableNagle));
+ DCHECK(!rv) << "Could not disable nagle";
+
+ // Enable TCP Keep-Alive to prevent NAT routers from timing out TCP
+ // connections. See http://crbug.com/27400 for details.
+
+ struct tcp_keepalive keepalive_vals = {
+ 1, // TCP keep-alive on.
+ 45000, // Wait 45s until sending first TCP keep-alive packet.
+ 45000, // Wait 45s between sending TCP keep-alive packets.
+ };
+ DWORD bytes_returned = 0xABAB;
+ rv = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &keepalive_vals,
+ sizeof(keepalive_vals), NULL, 0,
+ &bytes_returned, NULL, NULL);
+ DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket
+ << " [error: " << WSAGetLastError() << "].";
+
+ // Disregard any failure in disabling nagle or enabling TCP Keep-Alive.
+ return 0;
+}
+
+// Creates a new socket and sets default parameters for it. Returns
+// the OS error code (or 0 on success).
+int CreateSocket(int family, SOCKET* socket) {
+ *socket = WSASocket(family, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+ WSA_FLAG_OVERLAPPED);
+ if (*socket == INVALID_SOCKET) {
+ int os_error = WSAGetLastError();
+ LOG(ERROR) << "WSASocket failed: " << os_error;
+ return os_error;
+ }
+ int error = SetupSocket(*socket);
+ if (error) {
+ if (closesocket(*socket) < 0)
+ PLOG(ERROR) << "closesocket";
+ *socket = INVALID_SOCKET;
+ return error;
+ }
+ return 0;
+}
+
int MapConnectError(int os_error) {
switch (os_error) {
// connect fails with WSAEACCES when Windows Firewall blocks the
@@ -213,6 +316,7 @@ TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses,
net::NetLog* net_log,
const net::NetLog::Source& source)
: socket_(INVALID_SOCKET),
+ bound_socket_(INVALID_SOCKET),
addresses_(addresses),
current_ai_(NULL),
waiting_read_(false),
@@ -235,16 +339,52 @@ TCPClientSocketWin::~TCPClientSocketWin() {
net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL);
}
-void TCPClientSocketWin::AdoptSocket(SOCKET socket) {
+int TCPClientSocketWin::AdoptSocket(SOCKET socket) {
DCHECK_EQ(socket_, INVALID_SOCKET);
+
+ int error = SetupSocket(socket);
+ if (error)
+ return MapSystemError(error);
+
socket_ = socket;
- int error = SetupSocket();
- DCHECK_EQ(0, error);
core_ = new Core(this);
current_ai_ = addresses_.head();
use_history_.set_was_ever_connected();
+
+ return OK;
+}
+
+int TCPClientSocketWin::Bind(const IPEndPoint& address) {
+ if (current_ai_ != NULL || bind_address_.get()) {
+ // Cannot bind the socket if we are already connected or connecting.
+ return ERR_UNEXPECTED;
+ }
+
+ sockaddr_storage addr_storage;
+ sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage);
+ size_t addr_len = sizeof(addr_storage);
+ if (!address.ToSockAddr(addr, &addr_len))
+ return ERR_INVALID_ARGUMENT;
+
+ // Create |bound_socket_| and try to bound it to |address|.
+ int error = CreateSocket(address.GetFamily(), &bound_socket_);
+ if (error)
+ return MapSystemError(error);
+
+ if (bind(bound_socket_, addr, addr_len)) {
+ error = errno;
+ if (closesocket(bound_socket_) < 0)
+ PLOG(ERROR) << "closesocket";
+ bound_socket_ = INVALID_SOCKET;
+ return MapSystemError(error);
+ }
+
+ bind_address_.reset(new IPEndPoint(address));
+
+ return 0;
}
+
int TCPClientSocketWin::Connect(CompletionCallback* callback) {
DCHECK(CalledOnValidThread());
@@ -316,9 +456,25 @@ int TCPClientSocketWin::DoConnect() {
next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
- connect_os_error_ = CreateSocket(ai);
- if (connect_os_error_ != 0)
- return MapSystemError(connect_os_error_);
+ if (bound_socket_ != INVALID_SOCKET) {
+ DCHECK(bind_address_.get());
+ socket_ = bound_socket_;
+ bound_socket_ = INVALID_SOCKET;
+ } else {
+ connect_os_error_ = CreateSocket(ai->ai_family, &socket_);
+ if (connect_os_error_ != 0)
+ return MapSystemError(connect_os_error_);
+
+ if (bind_address_.get()) {
+ sockaddr_storage addr_storage;
+ sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage);
+ size_t addr_len = sizeof(addr_storage);
+ if (!bind_address_->ToSockAddr(addr, &addr_len))
+ return ERR_INVALID_ARGUMENT;
+ if (bind(socket_, addr, addr_len))
+ return MapSystemError(errno);
+ }
+ }
DCHECK(!core_);
core_ = new Core(this);
@@ -607,96 +763,12 @@ int TCPClientSocketWin::Write(IOBuffer* buf,
bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) {
DCHECK(CalledOnValidThread());
- int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
- reinterpret_cast<const char*>(&size), sizeof(size));
- DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError();
- return rv == 0;
+ return SetSocketReceiveBufferSize(socket_, size);
}
bool TCPClientSocketWin::SetSendBufferSize(int32 size) {
DCHECK(CalledOnValidThread());
- int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
- reinterpret_cast<const char*>(&size), sizeof(size));
- DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError();
- return rv == 0;
-}
-
-int TCPClientSocketWin::CreateSocket(const struct addrinfo* ai) {
- socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0,
- WSA_FLAG_OVERLAPPED);
- if (socket_ == INVALID_SOCKET) {
- int os_error = WSAGetLastError();
- LOG(ERROR) << "WSASocket failed: " << os_error;
- return os_error;
- }
- return SetupSocket();
-}
-
-int TCPClientSocketWin::SetupSocket() {
- // Increase the socket buffer sizes from the default sizes for WinXP. In
- // performance testing, there is substantial benefit by increasing from 8KB
- // to 64KB.
- // See also:
- // http://support.microsoft.com/kb/823764/EN-US
- // On Vista, if we manually set these sizes, Vista turns off its receive
- // window auto-tuning feature.
- // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx
- // Since Vista's auto-tune is better than any static value we can could set,
- // only change these on pre-vista machines.
- int32 major_version, minor_version, fix_version;
- base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
- &fix_version);
- if (major_version < 6) {
- const int32 kSocketBufferSize = 64 * 1024;
- SetReceiveBufferSize(kSocketBufferSize);
- SetSendBufferSize(kSocketBufferSize);
- }
-
- // Disable Nagle.
- // The Nagle implementation on windows is governed by RFC 896. The idea
- // behind Nagle is to reduce small packets on the network. When Nagle is
- // enabled, if a partial packet has been sent, the TCP stack will disallow
- // further *partial* packets until an ACK has been received from the other
- // side. Good applications should always strive to send as much data as
- // possible and avoid partial-packet sends. However, in most real world
- // applications, there are edge cases where this does not happen, and two
- // partil packets may be sent back to back. For a browser, it is NEVER
- // a benefit to delay for an RTT before the second packet is sent.
- //
- // As a practical example in Chromium today, consider the case of a small
- // POST. I have verified this:
- // Client writes 649 bytes of header (partial packet #1)
- // Client writes 50 bytes of POST data (partial packet #2)
- // In the above example, with Nagle, a RTT delay is inserted between these
- // two sends due to nagle. RTTs can easily be 100ms or more. The best
- // fix is to make sure that for POSTing data, we write as much data as
- // possible and minimize partial packets. We will fix that. But disabling
- // Nagle also ensure we don't run into this delay in other edge cases.
- // See also:
- // http://technet.microsoft.com/en-us/library/bb726981.aspx
- const BOOL kDisableNagle = TRUE;
- int rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY,
- reinterpret_cast<const char*>(&kDisableNagle),
- sizeof(kDisableNagle));
- DCHECK(!rv) << "Could not disable nagle";
-
- // Enable TCP Keep-Alive to prevent NAT routers from timing out TCP
- // connections. See http://crbug.com/27400 for details.
-
- struct tcp_keepalive keepalive_vals = {
- 1, // TCP keep-alive on.
- 45000, // Wait 45s until sending first TCP keep-alive packet.
- 45000, // Wait 45s between sending TCP keep-alive packets.
- };
- DWORD bytes_returned = 0xABAB;
- rv = WSAIoctl(socket_, SIO_KEEPALIVE_VALS, &keepalive_vals,
- sizeof(keepalive_vals), NULL, 0,
- &bytes_returned, NULL, NULL);
- DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket_
- << " [error: " << WSAGetLastError() << "].";
-
- // Disregard any failure in disabling nagle or enabling TCP Keep-Alive.
- return 0;
+ return SetSocketSendBufferSize(socket_, size);
}
void TCPClientSocketWin::LogConnectCompletion(int net_error) {