diff options
Diffstat (limited to 'net/socket/tcp_client_socket_win.cc')
-rw-r--r-- | net/socket/tcp_client_socket_win.cc | 256 |
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) { |