diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 20:48:07 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 20:48:07 +0000 |
commit | af0c32c60f49440e6c5bd6fb319e5db3ad011fc8 (patch) | |
tree | d27935898ccff3cfa274a403dbfc50ad7cafdbeb /net/udp | |
parent | 37cbcfeb1c02033b2785c7aeb321f99ad9f65821 (diff) | |
download | chromium_src-af0c32c60f49440e6c5bd6fb319e5db3ad011fc8.zip chromium_src-af0c32c60f49440e6c5bd6fb319e5db3ad011fc8.tar.gz chromium_src-af0c32c60f49440e6c5bd6fb319e5db3ad011fc8.tar.bz2 |
UDP sockets implementation for windows.
BUG=None
TEST=Unittests
Review URL: http://codereview.chromium.org/6658027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77855 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/udp')
-rw-r--r-- | net/udp/datagram_server_socket.h | 2 | ||||
-rw-r--r-- | net/udp/udp_socket.h | 19 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.cc | 42 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.h | 24 | ||||
-rw-r--r-- | net/udp/udp_socket_unittest.cc | 64 | ||||
-rw-r--r-- | net/udp/udp_socket_win.cc | 366 | ||||
-rw-r--r-- | net/udp/udp_socket_win.h | 178 |
7 files changed, 643 insertions, 52 deletions
diff --git a/net/udp/datagram_server_socket.h b/net/udp/datagram_server_socket.h index 30286e5..9841d53 100644 --- a/net/udp/datagram_server_socket.h +++ b/net/udp/datagram_server_socket.h @@ -6,8 +6,6 @@ #define NET_UDP_DATAGRAM_SERVER_SOCKET_H_ #pragma once -#include <sys/socket.h> - #include "net/base/completion_callback.h" #include "net/udp/datagram_socket.h" diff --git a/net/udp/udp_socket.h b/net/udp/udp_socket.h index 9e5f826..15ba76c 100644 --- a/net/udp/udp_socket.h +++ b/net/udp/udp_socket.h @@ -16,7 +16,24 @@ namespace net { -// A client socket that uses UDP as the transport layer. +// UDPSocket +// Accessor API for a UDP socket in either client or server form. +// +// Client form: +// In this case, we're connecting to a specific server, so the client will +// usually use: +// Connect(address) // Connect to a UDP server +// Read/Write // Reads/Writes all go to a single destination +// +// Server form: +// In this case, we want to read/write to many clients which are connecting +// to this server. First the server 'binds' to an addres, then we read from +// clients and write responses to them. +// Example: +// Bind(address/port) // Binds to port for reading from clients +// RecvFrom/SendTo // Each read can come from a different client +// // Writes need to be directed to a specific +// // address. #if defined(OS_WIN) typedef UDPSocketWin UDPSocket; #elif defined(OS_POSIX) diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc index 7c75ce3..de24253 100644 --- a/net/udp/udp_socket_libevent.cc +++ b/net/udp/udp_socket_libevent.cc @@ -63,6 +63,8 @@ int MapPosixError(int os_error) { return ERR_ADDRESS_INVALID; case EMSGSIZE: return ERR_MSG_TOO_BIG; + case ENOTCONN: + return ERR_SOCKET_NOT_CONNECTED; case 0: return OK; default: @@ -99,9 +101,21 @@ UDPSocketLibevent::~UDPSocketLibevent() { } void UDPSocketLibevent::Close() { + DCHECK(CalledOnValidThread()); + + if (read_callback_) + DoReadCallback(ERR_ABORTED); + if (write_callback_) + DoReadCallback(ERR_ABORTED); + if (!is_connected()) return; + bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); + DCHECK(ok); + ok = write_socket_watcher_.StopWatchingFileDescriptor(); + DCHECK(ok); + if (HANDLE_EINTR(close(socket_)) < 0) PLOG(ERROR) << "close"; @@ -161,10 +175,7 @@ int UDPSocketLibevent::Read(IOBuffer* buf, DCHECK(callback); // Synchronous operation not supported DCHECK_GT(buf_len, 0); - read_buf_ = buf; - read_buf_len_ = buf_len; - - int nread = InternalRead(); + int nread = InternalRead(buf, buf_len); if (nread != ERR_IO_PENDING) return nread; @@ -175,6 +186,8 @@ int UDPSocketLibevent::Read(IOBuffer* buf, return MapPosixError(errno); } + read_buf_ = buf; + read_buf_len_ = buf_len; read_callback_ = callback; return ERR_IO_PENDING; } @@ -232,7 +245,7 @@ int UDPSocketLibevent::Connect(const IPEndPoint& address) { DCHECK(!remote_address_.get()); int rv = CreateSocket(address); if (rv < 0) - return MapPosixError(rv); + return rv; struct sockaddr_storage addr_storage; size_t addr_len = sizeof(addr_storage); @@ -242,7 +255,7 @@ int UDPSocketLibevent::Connect(const IPEndPoint& address) { rv = HANDLE_EINTR(connect(socket_, addr, addr_len)); if (rv < 0) - return MapPosixError(rv); + return MapPosixError(errno); remote_address_.reset(new IPEndPoint(address)); return rv; @@ -253,7 +266,7 @@ int UDPSocketLibevent::Bind(const IPEndPoint& address) { DCHECK(!local_address_.get()); int rv = CreateSocket(address); if (rv < 0) - return MapPosixError(rv); + return rv; struct sockaddr_storage addr_storage; size_t addr_len = sizeof(addr_storage); @@ -263,7 +276,7 @@ int UDPSocketLibevent::Bind(const IPEndPoint& address) { rv = bind(socket_, addr, addr_len); if (rv < 0) - return MapPosixError(rv); + return MapPosixError(errno); local_address_.reset(new IPEndPoint(address)); return rv; @@ -292,7 +305,7 @@ void UDPSocketLibevent::DoWriteCallback(int rv) { } void UDPSocketLibevent::DidCompleteRead() { - int result = InternalRead(); + int result = InternalRead(read_buf_, read_buf_len_); if (result != ERR_IO_PENDING) { read_buf_ = NULL; read_buf_len_ = 0; @@ -305,9 +318,9 @@ void UDPSocketLibevent::DidCompleteRead() { int UDPSocketLibevent::CreateSocket(const IPEndPoint& address) { socket_ = socket(address.GetFamily(), SOCK_DGRAM, 0); if (socket_ == kInvalidSocket) - return errno; + return MapPosixError(errno); if (SetNonBlocking(socket_)) { - const int err = errno; + const int err = MapPosixError(errno); Close(); return err; } @@ -331,7 +344,7 @@ void UDPSocketLibevent::DidCompleteWrite() { } } -int UDPSocketLibevent::InternalRead() { +int UDPSocketLibevent::InternalRead(IOBuffer* buf, int buf_len) { int bytes_transferred; int flags = 0; @@ -341,8 +354,8 @@ int UDPSocketLibevent::InternalRead() { bytes_transferred = HANDLE_EINTR(recvfrom(socket_, - read_buf_->data(), - read_buf_len_, + buf->data(), + buf_len, flags, addr, &addr_len)); @@ -360,6 +373,7 @@ int UDPSocketLibevent::InternalRead() { } return result; } + int UDPSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { struct sockaddr_storage addr_storage; size_t addr_len = sizeof(addr_storage); diff --git a/net/udp/udp_socket_libevent.h b/net/udp/udp_socket_libevent.h index ebe01b2..1a9fd6e 100644 --- a/net/udp/udp_socket_libevent.h +++ b/net/udp/udp_socket_libevent.h @@ -6,25 +6,6 @@ #define NET_UDP_UDP_SOCKET_LIBEVENT_H_ #pragma once -// UDPSocketLibevent -// Accessor API for a UDP socket in either client or server form. -// -// Client form: -// In this case, we're connecting to a specific server, so the client will -// usually use: -// Connect(address) // Connect to a UDP server -// Read/Write // Reads/Writes all go to a single destination -// -// Server form: -// In this case, we want to read/write to many clients which are connecting -// to this server. First the server 'binds' to an addres, then we read from -// clients and write responses to them. -// Example: -// Bind(address/port) // Binds to port for reading from clients -// RecvFrom/SendTo // Each read can come from a different client -// // Writes need to be directed to a specific -// // address. - #include "base/message_loop.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" @@ -33,7 +14,6 @@ #include "net/base/ip_endpoint.h" #include "net/base/net_log.h" #include "net/socket/client_socket.h" -#include "net/udp/datagram_socket.h" namespace net { @@ -113,8 +93,6 @@ class UDPSocketLibevent : public base::NonThreadSafe { // Returns true if the socket is already connected or bound. bool is_connected() const { return socket_ != kInvalidSocket; } - IPEndPoint* local_address() { return local_address_.get(); } - private: static const int kInvalidSocket = -1; @@ -164,7 +142,7 @@ class UDPSocketLibevent : public base::NonThreadSafe { // Returns the OS error code (or 0 on success). int CreateSocket(const IPEndPoint& address); - int InternalRead(); + int InternalRead(IOBuffer* buf, int buf_len); int InternalWrite(IOBuffer* buf, int buf_len); int socket_; diff --git a/net/udp/udp_socket_unittest.cc b/net/udp/udp_socket_unittest.cc index ef43731..0f21521 100644 --- a/net/udp/udp_socket_unittest.cc +++ b/net/udp/udp_socket_unittest.cc @@ -107,7 +107,7 @@ class UDPSocketTest : public PlatformTest { return bytes_sent; } - private: + protected: static const int kMaxRead = 1024; scoped_refptr<IOBufferWithSize> buffer_; IPEndPoint recv_from_address_; @@ -223,14 +223,17 @@ TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) { struct TestData { std::string remote_address; std::string local_address; - bool is_ipv6; + bool may_fail; } tests[] = { { "127.0.00.1", "127.0.0.1", false }, { "192.168.1.1", "127.0.0.1", false }, - { "::1", "::1", true }, + { "::1", "::1", false }, { "2001:db8:0::42", "::1", true }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); i++) { + SCOPED_TRACE(std::string("Connecting from ") + tests[i].local_address + + std::string(" to ") + tests[i].remote_address); + net::IPAddressNumber ip_number; net::ParseIPLiteralToNumber(tests[i].remote_address, &ip_number); net::IPEndPoint remote_address(ip_number, 80); @@ -239,6 +242,12 @@ TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) { UDPClientSocket client(NULL, NetLog::Source()); int rv = client.Connect(remote_address); + if (tests[i].may_fail && rv == ERR_ADDRESS_UNREACHABLE) { + // Connect() may return ERR_ADDRESS_UNREACHABLE for IPv6 + // addresses if IPv6 is not configured. + continue; + } + EXPECT_LE(ERR_IO_PENDING, rv); IPEndPoint fetched_local_address; @@ -260,21 +269,52 @@ TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) { } TEST_F(UDPSocketTest, ServerGetLocalAddress) { - // TODO(mbelshe): implement me + IPEndPoint bind_address; + CreateUDPAddress("127.0.0.1", 0, &bind_address); + UDPServerSocket server(NULL, NetLog::Source()); + int rv = server.Listen(bind_address); + EXPECT_EQ(OK, rv); + + IPEndPoint local_address; + rv = server.GetLocalAddress(&local_address); + EXPECT_EQ(rv, 0); + + // Verify that port was allocated. + EXPECT_GE(local_address.port(), 0); + EXPECT_EQ(local_address.address(), bind_address.address()); } TEST_F(UDPSocketTest, ServerGetPeerAddress) { - // TODO(mbelshe): implement me + IPEndPoint bind_address; + CreateUDPAddress("127.0.0.1", 0, &bind_address); + UDPServerSocket server(NULL, NetLog::Source()); + int rv = server.Listen(bind_address); + EXPECT_EQ(OK, rv); + + IPEndPoint peer_address; + rv = server.GetPeerAddress(&peer_address); + EXPECT_EQ(rv, ERR_SOCKET_NOT_CONNECTED); } -} // namespace +// Close the socket while read is pending. +TEST_F(UDPSocketTest, CloseWithPendingRead) { + IPEndPoint bind_address; + CreateUDPAddress("127.0.0.1", 0, &bind_address); + UDPServerSocket server(NULL, NetLog::Source()); + int rv = server.Listen(bind_address); + EXPECT_EQ(OK, rv); -} // namespace net + TestCompletionCallback callback; + IPEndPoint from; + rv = server.RecvFrom(buffer_, kMaxRead, &from, &callback); + EXPECT_EQ(rv, ERR_IO_PENDING); -int main(int argc, char** argv) { - // Record histograms, so we can get histograms data in tests. - base::StatisticsRecorder recorder; - NetTestSuite test_suite(argc, argv); + server.Close(); - return test_suite.Run(); + EXPECT_TRUE(callback.have_result()); + EXPECT_EQ(callback.GetResult(rv), ERR_ABORTED); } + +} // namespace + +} // namespace net diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc new file mode 100644 index 0000000..8a180d2 --- /dev/null +++ b/net/udp/udp_socket_win.cc @@ -0,0 +1,366 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/udp/udp_socket_win.h" + +#include <mstcpip.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/memory_debug.h" +#include "base/metrics/stats_counters.h" +#include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/base/net_util.h" +#include "net/base/winsock_init.h" +#include "net/base/winsock_util.h" + +namespace net { + +void UDPSocketWin::ReadDelegate::OnObjectSignaled(HANDLE object) { + DCHECK_EQ(object, socket_->read_overlapped_.hEvent); + socket_->DidCompleteRead(); +} + +void UDPSocketWin::WriteDelegate::OnObjectSignaled(HANDLE object) { + DCHECK_EQ(object, socket_->write_overlapped_.hEvent); + socket_->DidCompleteWrite(); +} + +UDPSocketWin::UDPSocketWin(net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(INVALID_SOCKET), + ALLOW_THIS_IN_INITIALIZER_LIST(read_delegate_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(write_delegate_(this)), + recv_from_address_(NULL), + read_callback_(NULL), + write_callback_(NULL), + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) { + EnsureWinsockInit(); + scoped_refptr<NetLog::EventParameters> params; + if (source.is_valid()) + params = new NetLogSourceParameter("source_dependency", source); + net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, params); + memset(&read_overlapped_, 0, sizeof(read_overlapped_)); + read_overlapped_.hEvent = WSACreateEvent(); + memset(&write_overlapped_, 0, sizeof(write_overlapped_)); + write_overlapped_.hEvent = WSACreateEvent(); +} + +UDPSocketWin::~UDPSocketWin() { + Close(); + net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); +} + +void UDPSocketWin::Close() { + DCHECK(CalledOnValidThread()); + + if (!is_connected()) + return; + + if (read_callback_) + DoReadCallback(ERR_ABORTED); + if (write_callback_) + DoReadCallback(ERR_ABORTED); + + read_watcher_.StopWatching(); + write_watcher_.StopWatching(); + + closesocket(socket_); + socket_ = INVALID_SOCKET; +} + +int UDPSocketWin::GetPeerAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + if (!remote_address_.get()) { + struct sockaddr_storage addr_storage; + int addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (getpeername(socket_, addr, &addr_len)) + return MapWinsockError(WSAGetLastError()); + scoped_ptr<IPEndPoint> address(new IPEndPoint()); + if (!address->FromSockAddr(addr, addr_len)) + return ERR_FAILED; + remote_address_.reset(address.release()); + } + + *address = *remote_address_; + return OK; +} + +int UDPSocketWin::GetLocalAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + if (!local_address_.get()) { + struct sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (getsockname(socket_, addr, &addr_len)) + return MapWinsockError(WSAGetLastError()); + scoped_ptr<IPEndPoint> address(new IPEndPoint()); + if (!address->FromSockAddr(addr, addr_len)) + return ERR_FAILED; + local_address_.reset(address.release()); + } + + *address = *local_address_; + return OK; +} + +int UDPSocketWin::RecvFrom(IOBuffer* buf, + int buf_len, + IPEndPoint* address, + CompletionCallback* callback) { + DCHECK(!recv_from_address_); + recv_from_address_ = address; + return Read(buf, buf_len, callback); +} + +int UDPSocketWin::Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(INVALID_SOCKET, socket_); + DCHECK(!read_callback_); + DCHECK(callback); // Synchronous operation not supported. + DCHECK_GT(buf_len, 0); + + int nread = InternalRead(buf, buf_len); + if (nread != ERR_IO_PENDING) + return nread; + + read_iobuffer_ = buf; + read_callback_ = callback; + return ERR_IO_PENDING; +} + +int UDPSocketWin::SendTo(IOBuffer* buf, + int buf_len, + const IPEndPoint& address, + CompletionCallback* callback) { + send_to_address_.reset(new IPEndPoint(address)); + return Write(buf, buf_len, callback); +} + +int UDPSocketWin::Write(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(INVALID_SOCKET, socket_); + DCHECK(!write_callback_); + DCHECK(callback); // Synchronous operation not supported. + DCHECK_GT(buf_len, 0); + + int nwrite = InternalWrite(buf, buf_len); + if (nwrite != ERR_IO_PENDING) + return nwrite; + + write_iobuffer_ = buf; + write_callback_ = callback; + return ERR_IO_PENDING; +} + +int UDPSocketWin::Connect(const IPEndPoint& address) { + DCHECK(!is_connected()); + DCHECK(!remote_address_.get()); + int rv = CreateSocket(address); + if (rv < 0) + return rv; + + struct sockaddr_storage addr_storage; + size_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_FAILED; + + rv = connect(socket_, addr, addr_len); + if (rv < 0) + return MapWinsockError(WSAGetLastError()); + + remote_address_.reset(new IPEndPoint(address)); + return rv; +} + +int UDPSocketWin::Bind(const IPEndPoint& address) { + DCHECK(!is_connected()); + DCHECK(!local_address_.get()); + int rv = CreateSocket(address); + if (rv < 0) + return rv; + + struct sockaddr_storage addr_storage; + size_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_FAILED; + + rv = bind(socket_, addr, addr_len); + if (rv < 0) + return MapWinsockError(WSAGetLastError()); + + local_address_.reset(new IPEndPoint(address)); + return rv; +} + +int UDPSocketWin::CreateSocket(const IPEndPoint& address) { + socket_ = WSASocket(address.GetFamily(), SOCK_DGRAM, IPPROTO_UDP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (socket_ == INVALID_SOCKET) + return MapWinsockError(WSAGetLastError()); + return OK; +} + +void UDPSocketWin::DoReadCallback(int rv) { + DCHECK_NE(rv, ERR_IO_PENDING); + DCHECK(read_callback_); + + // since Run may result in Read being called, clear read_callback_ up front. + CompletionCallback* c = read_callback_; + read_callback_ = NULL; + recv_from_address_ = NULL; + c->Run(rv); +} + +void UDPSocketWin::DoWriteCallback(int rv) { + DCHECK_NE(rv, ERR_IO_PENDING); + DCHECK(write_callback_); + + // since Run may result in Write being called, clear write_callback_ up front. + CompletionCallback* c = write_callback_; + write_callback_ = NULL; + send_to_address_.reset(); + c->Run(rv); +} + +void UDPSocketWin::DidCompleteRead() { + DWORD num_bytes, flags; + BOOL ok = WSAGetOverlappedResult(socket_, &read_overlapped_, + &num_bytes, FALSE, &flags); + WSAResetEvent(read_overlapped_.hEvent); + int result = ok ? num_bytes : MapWinsockError(WSAGetLastError()); + if (ok) { + if (!ProcessSuccessfulRead(num_bytes)) + result = ERR_FAILED; + } + read_iobuffer_ = NULL; + DoReadCallback(result); +} + +bool UDPSocketWin::ProcessSuccessfulRead(int num_bytes) { + static base::StatsCounter read_bytes("udp.read_bytes"); + read_bytes.Add(num_bytes); + + // Convert address. + if (recv_from_address_) { + struct sockaddr* addr = + reinterpret_cast<struct sockaddr*>(&recv_addr_storage_); + if (!recv_from_address_->FromSockAddr(addr, recv_addr_len_)) + return false; + } + + return true; +} + +void UDPSocketWin::DidCompleteWrite() { + DWORD num_bytes, flags; + BOOL ok = WSAGetOverlappedResult(socket_, &write_overlapped_, + &num_bytes, FALSE, &flags); + WSAResetEvent(write_overlapped_.hEvent); + int result = ok ? num_bytes : MapWinsockError(WSAGetLastError()); + if (ok) + ProcessSuccessfulWrite(num_bytes); + write_iobuffer_ = NULL; + DoWriteCallback(result); +} + +void UDPSocketWin::ProcessSuccessfulWrite(int num_bytes) { + static base::StatsCounter write_bytes("udp.write_bytes"); + write_bytes.Add(num_bytes); +} + +int UDPSocketWin::InternalRead(IOBuffer* buf, int buf_len) { + recv_addr_len_ = sizeof(recv_addr_storage_); + struct sockaddr* addr = + reinterpret_cast<struct sockaddr*>(&recv_addr_storage_); + + WSABUF read_buffer; + read_buffer.buf = buf->data(); + read_buffer.len = buf_len; + + DWORD flags = 0; + DWORD num; + AssertEventNotSignaled(read_overlapped_.hEvent); + int rv = WSARecvFrom(socket_, &read_buffer, 1, &num, &flags, addr, + &recv_addr_len_, &read_overlapped_, NULL); + if (rv == 0) { + if (ResetEventIfSignaled(read_overlapped_.hEvent)) { + // Because of how WSARecv fills memory when used asynchronously, Purify + // isn't able to detect that it's been initialized, so it scans for 0xcd + // in the buffer and reports UMRs (uninitialized memory reads) for those + // individual bytes. We override that in PURIFY builds to avoid the + // false error reports. + // See bug 5297. + base::MemoryDebug::MarkAsInitialized(read_buffer.buf, num); + if (!ProcessSuccessfulRead(num)) + return ERR_FAILED; + return static_cast<int>(num); + } + } else { + int os_error = WSAGetLastError(); + if (os_error != WSA_IO_PENDING) + return MapWinsockError(os_error); + } + read_watcher_.StartWatching(read_overlapped_.hEvent, &read_delegate_); + return ERR_IO_PENDING; +} + +int UDPSocketWin::InternalWrite(IOBuffer* buf, int buf_len) { + struct sockaddr_storage addr_storage; + size_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + + // Convert address. + if (!send_to_address_.get()) { + addr = NULL; + addr_len = 0; + } else { + if (!send_to_address_->ToSockAddr(addr, &addr_len)) + return ERR_FAILED; + } + + WSABUF write_buffer; + write_buffer.buf = buf->data(); + write_buffer.len = buf_len; + + DWORD flags = 0; + DWORD num; + AssertEventNotSignaled(write_overlapped_.hEvent); + int rv = WSASendTo(socket_, &write_buffer, 1, &num, flags, + addr, addr_len, &write_overlapped_, NULL); + if (rv == 0) { + if (ResetEventIfSignaled(write_overlapped_.hEvent)) { + ProcessSuccessfulWrite(num); + return static_cast<int>(num); + } + } else { + int os_error = WSAGetLastError(); + if (os_error != WSA_IO_PENDING) + return MapWinsockError(os_error); + } + + write_watcher_.StartWatching(write_overlapped_.hEvent, &write_delegate_); + return ERR_IO_PENDING; +} + +} // namespace net diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h new file mode 100644 index 0000000..d9e1dee --- /dev/null +++ b/net/udp/udp_socket_win.h @@ -0,0 +1,178 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_UDP_UDP_SOCKET_WIN_H_ +#define NET_UDP_UDP_SOCKET_WIN_H_ +#pragma once + +#include <winsock2.h> + +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "base/win/object_watcher.h" +#include "net/base/completion_callback.h" +#include "net/base/ip_endpoint.h" +#include "net/base/io_buffer.h" +#include "net/base/net_log.h" + +namespace net { + +class BoundNetLog; + +class UDPSocketWin : public base::NonThreadSafe { + public: + UDPSocketWin(net::NetLog* net_log, + const net::NetLog::Source& source); + virtual ~UDPSocketWin(); + + // Connect the socket to connect with a certain |address|. + // Returns a net error code. + int Connect(const IPEndPoint& address); + + // Bind the address/port for this socket to |address|. This is generally + // only used on a server. + // Returns a net error code. + int Bind(const IPEndPoint& address); + + // Close the socket. + void Close(); + + // Copy the remote udp address into |address| and return a network error code. + int GetPeerAddress(IPEndPoint* address) const; + + // Copy the local udp address into |address| and return a network error code. + // (similar to getsockname) + int GetLocalAddress(IPEndPoint* address) const; + + // IO: + // Multiple outstanding read requests are not supported. + // Full duplex mode (reading and writing at the same time) is supported + + // Read from the socket. + // Only usable from the client-side of a UDP socket, after the socket + // has been connected. + int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); + + // Write to the socket. + // Only usable from the client-side of a UDP socket, after the socket + // has been connected. + int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback); + + // Read from a socket and receive sender address information. + // |buf| is the buffer to read data into. + // |buf_len| is the maximum amount of data to read. + // |address| is a buffer provided by the caller for receiving the sender + // address information about the received data. This buffer must be kept + // alive by the caller until the callback is placed. + // |address_length| is a ptr to the length of the |address| buffer. This + // is an input parameter containing the maximum size |address| can hold + // and also an output parameter for the size of |address| upon completion. + // |callback| the callback on completion of the Recv. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, + // and |address_length| alive until the callback is called. + int RecvFrom(IOBuffer* buf, + int buf_len, + IPEndPoint* address, + CompletionCallback* callback); + + // Send to a socket with a particular destination. + // |buf| is the buffer to send + // |buf_len| is the number of bytes to send + // |address| is the recipient address. + // |address_length| is the size of the recipient address + // |callback| is the user callback function to call on complete. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address| + // alive until the callback is called. + int SendTo(IOBuffer* buf, + int buf_len, + const IPEndPoint& address, + CompletionCallback* callback); + + // Returns true if the socket is already connected or bound. + bool is_connected() const { return socket_ != INVALID_SOCKET; } + + private: + class ReadDelegate : public base::win::ObjectWatcher::Delegate { + public: + explicit ReadDelegate(UDPSocketWin* socket) : socket_(socket) {} + virtual ~ReadDelegate() {} + + // base::ObjectWatcher::Delegate methods: + virtual void OnObjectSignaled(HANDLE object); + + private: + UDPSocketWin* const socket_; + }; + + class WriteDelegate : public base::win::ObjectWatcher::Delegate { + public: + explicit WriteDelegate(UDPSocketWin* socket) : socket_(socket) {} + virtual ~WriteDelegate() {} + + // base::ObjectWatcher::Delegate methods: + virtual void OnObjectSignaled(HANDLE object); + + private: + UDPSocketWin* const socket_; + }; + + void DoReadCallback(int rv); + void DoWriteCallback(int rv); + void DidCompleteRead(); + void DidCompleteWrite(); + bool ProcessSuccessfulRead(int num_bytes); + void ProcessSuccessfulWrite(int num_bytes); + + // Returns the OS error code (or 0 on success). + int CreateSocket(const IPEndPoint& address); + + int InternalRead(IOBuffer* buf, int buf_len); + int InternalWrite(IOBuffer* buf, int buf_len); + + SOCKET socket_; + + // These are mutable since they're just cached copies to make + // GetPeerAddress/GetLocalAddress smarter. + mutable scoped_ptr<IPEndPoint> local_address_; + mutable scoped_ptr<IPEndPoint> remote_address_; + + // The socket's win wrappers + ReadDelegate read_delegate_; + WriteDelegate write_delegate_; + + // Watchers to watch for events from Read() and Write(). + base::win::ObjectWatcher read_watcher_; + base::win::ObjectWatcher write_watcher_; + + // OVERLAPPED for pending read and write operations. + OVERLAPPED read_overlapped_; + OVERLAPPED write_overlapped_; + + // The buffer used by InternalRead() to retry Read requests + scoped_refptr<IOBuffer> read_iobuffer_; + struct sockaddr_storage recv_addr_storage_; + socklen_t recv_addr_len_; + IPEndPoint* recv_from_address_; + + // The buffer used by InternalWrite() to retry Write requests + scoped_refptr<IOBuffer> write_iobuffer_; + scoped_ptr<IPEndPoint> send_to_address_; + + // External callback; called when read is complete. + CompletionCallback* read_callback_; + + // External callback; called when write is complete. + CompletionCallback* write_callback_; + + BoundNetLog net_log_; + + DISALLOW_COPY_AND_ASSIGN(UDPSocketWin); +}; + +} // namespace net + +#endif // NET_UDP_UDP_SOCKET_WIN_H_ |