diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host.h | 9 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp.cc | 72 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp.h | 3 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc | 13 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_udp.cc | 41 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_udp.h | 10 | ||||
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_udp_unittest.cc | 11 | ||||
-rw-r--r-- | content/common/p2p_messages.h | 3 | ||||
-rw-r--r-- | content/renderer/p2p/ipc_socket_factory.cc | 45 | ||||
-rw-r--r-- | content/renderer/p2p/socket_client.cc | 13 | ||||
-rw-r--r-- | content/renderer/p2p/socket_client.h | 7 | ||||
-rw-r--r-- | content/renderer/p2p/socket_dispatcher.cc | 8 | ||||
-rw-r--r-- | content/renderer/p2p/socket_dispatcher.h | 1 |
13 files changed, 152 insertions, 84 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host.h b/content/browser/renderer_host/p2p/socket_host.h index 0abb74e..9fe985d 100644 --- a/content/browser/renderer_host/p2p/socket_host.h +++ b/content/browser/renderer_host/p2p/socket_host.h @@ -62,15 +62,6 @@ class CONTENT_EXPORT P2PSocketHost { STATE_ERROR, }; - // Maximum size of send buffers. Must be big enough to fit data for - // one data burst. Send buffers size needs to be limited to prevent - // from consuming too much memory with misbehaving renderer process. - // - // TODO(sergeyu): Consider implementing congestion notifications to - // minimize buffering. This will require some fixes in libjingle, - // see crbug.com/91495 . - static const int kMaxSendBufferSize = 256 * 1024; - P2PSocketHost(IPC::Sender* message_sender, int id); // Verifies that the packet |data| has a valid STUN header. In case diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc index 2409280..be59f72 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -87,10 +87,6 @@ void P2PSocketHostTcp::OnConnected(int result) { return; } - if (!socket_->SetSendBufferSize(kMaxSendBufferSize)) { - LOG(WARNING) << "Failed to set send buffer size for TCP socket."; - } - net::IPEndPoint address; result = socket_->GetLocalAddress(&address); if (result < 0) { @@ -196,16 +192,8 @@ void P2PSocketHostTcp::Send(const net::IPEndPoint& to, return; } - if (write_buffer_) { - // Silently drop packet if we haven't finished sending previous - // packet. - VLOG(1) << "Dropping TCP packet."; - return; - } - if (!(to == remote_address_)) { - // Renderer should use this socket only to send data to - // |remote_address_|. + // Renderer should use this socket only to send data to |remote_address_|. NOTREACHED(); OnError(); return; @@ -223,51 +211,51 @@ void P2PSocketHostTcp::Send(const net::IPEndPoint& to, } int size = kPacketHeaderSize + data.size(); - write_buffer_ = new net::DrainableIOBuffer(new net::IOBuffer(size), size); - *reinterpret_cast<uint16*>(write_buffer_->data()) = - base::HostToNet16(data.size()); - memcpy(write_buffer_->data() + kPacketHeaderSize, &data[0], data.size()); + scoped_refptr<net::DrainableIOBuffer> buffer = + new net::DrainableIOBuffer(new net::IOBuffer(size), size); + *reinterpret_cast<uint16*>(buffer->data()) = base::HostToNet16(data.size()); + memcpy(buffer->data() + kPacketHeaderSize, &data[0], data.size()); + if (write_buffer_) { + write_queue_.push(buffer); + return; + } + + write_buffer_ = buffer; DoWrite(); } void P2PSocketHostTcp::DoWrite() { - while (true) { + while (write_buffer_ && state_ == STATE_OPEN) { int result = socket_->Write(write_buffer_, write_buffer_->BytesRemaining(), base::Bind(&P2PSocketHostTcp::OnWritten, base::Unretained(this))); - if (result >= 0) { - write_buffer_->DidConsume(result); - if (write_buffer_->BytesRemaining() == 0) { - write_buffer_ = NULL; - break; - } - } else { - if (result != net::ERR_IO_PENDING) { - LOG(ERROR) << "Error when sending data in TCP socket: " << result; - OnError(); - } - break; - } + HandleWriteResult(result); } } void P2PSocketHostTcp::OnWritten(int result) { - DCHECK(write_buffer_); DCHECK_NE(result, net::ERR_IO_PENDING); + HandleWriteResult(result); + DoWrite(); +} - if (result < 0) { - DCHECK(result != net::ERR_IO_PENDING); +void P2PSocketHostTcp::HandleWriteResult(int result) { + DCHECK(write_buffer_); + if (result >= 0) { + write_buffer_->DidConsume(result); + if (write_buffer_->BytesRemaining() == 0) { + message_sender_->Send(new P2PMsg_OnSendComplete(id_)); + if (write_queue_.empty()) { + write_buffer_ = NULL; + } else { + write_buffer_ = write_queue_.front(); + write_queue_.pop(); + } + } + } else if (result != net::ERR_IO_PENDING) { LOG(ERROR) << "Error when sending data in TCP socket: " << result; OnError(); - return; - } - - write_buffer_->DidConsume(result); - if (write_buffer_->BytesRemaining() == 0) { - write_buffer_ = NULL; - } else { - DoWrite(); } } diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.h b/content/browser/renderer_host/p2p/socket_host_tcp.h index 48cc7f9..0b129cc 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.h +++ b/content/browser/renderer_host/p2p/socket_host_tcp.h @@ -51,6 +51,7 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHost { void OnPacket(std::vector<char>& data); void DoWrite(); + void HandleWriteResult(int result); // Callbacks for Connect(), Read() and Write(). void OnConnected(int result); @@ -61,6 +62,8 @@ class CONTENT_EXPORT P2PSocketHostTcp : public P2PSocketHost { scoped_ptr<net::StreamSocket> socket_; scoped_refptr<net::GrowableIOBuffer> read_buffer_; + + std::queue<scoped_refptr<net::DrainableIOBuffer> > write_queue_; scoped_refptr<net::DrainableIOBuffer> write_buffer_; bool connected_; diff --git a/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc b/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc index 86e1d97..af4950c 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp_unittest.cc @@ -62,6 +62,11 @@ class P2PSocketHostTcpTest : public testing::Test { // Verify that we can send STUN message and that they are formatted // properly. TEST_F(P2PSocketHostTcpTest, SendStunNoAuth) { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(3) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + std::vector<char> packet1; CreateStunRequest(&packet1); socket_host_->Send(dest_, packet1); @@ -88,6 +93,11 @@ TEST_F(P2PSocketHostTcpTest, SendStunNoAuth) { // Verify that we can receive STUN messages from the socket, and that // the messages are parsed properly. TEST_F(P2PSocketHostTcpTest, ReceiveStun) { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(3) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + std::vector<char> packet1; CreateStunRequest(&packet1); socket_host_->Send(dest_, packet1); @@ -152,6 +162,9 @@ TEST_F(P2PSocketHostTcpTest, SendAfterStunRequest) { received_data.append(IntToSize(request_packet.size())); received_data.append(request_packet.begin(), request_packet.end()); + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); EXPECT_CALL(sender_, Send(MatchPacketMessage(request_packet))) .WillOnce(DoAll(DeleteArg<0>(), Return(true))); socket_->AppendInputData(&received_data[0], received_data.size()); diff --git a/content/browser/renderer_host/p2p/socket_host_udp.cc b/content/browser/renderer_host/p2p/socket_host_udp.cc index b84ee68..6a9a93d 100644 --- a/content/browser/renderer_host/p2p/socket_host_udp.cc +++ b/content/browser/renderer_host/p2p/socket_host_udp.cc @@ -42,7 +42,6 @@ P2PSocketHostUdp::PendingPacket::~PendingPacket() { P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender, int id) : P2PSocketHost(message_sender, id), socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())), - send_queue_bytes_(0), send_pending_(false) { } @@ -102,18 +101,18 @@ void P2PSocketHostUdp::DoRead() { base::Unretained(this))); if (result == net::ERR_IO_PENDING) return; - DidCompleteRead(result); + HandleReadResult(result); } while (state_ == STATE_OPEN); } void P2PSocketHostUdp::OnRecv(int result) { - DidCompleteRead(result); + HandleReadResult(result); if (state_ == STATE_OPEN) { DoRead(); } } -void P2PSocketHostUdp::DidCompleteRead(int result) { +void P2PSocketHostUdp::HandleReadResult(int result) { DCHECK_EQ(state_, STATE_OPEN); if (result > 0) { @@ -159,13 +158,7 @@ void P2PSocketHostUdp::Send(const net::IPEndPoint& to, } if (send_pending_) { - if (send_queue_bytes_ + static_cast<int>(data.size()) > - kMaxSendBufferSize) { - LOG(WARNING) << "Send buffer is full. Dropping a packet."; - return; - } send_queue_.push_back(PendingPacket(to, data)); - send_queue_bytes_ += data.size(); } else { PendingPacket packet(to, data); DoSend(packet); @@ -188,12 +181,8 @@ void P2PSocketHostUdp::DoSend(const PendingPacket& packet) { if (result == net::ERR_IO_PENDING) { send_pending_ = true; - } else if (IsTransientError(result)) { - LOG(INFO) << "sendto() has failed twice returning a " - " transient error. Dropping the packet."; - } else if (result < 0) { - LOG(ERROR) << "Error when sending data in UDP socket: " << result; - OnError(); + } else { + HandleSendResult(result); } } @@ -203,19 +192,27 @@ void P2PSocketHostUdp::OnSend(int result) { send_pending_ = false; - if (result < 0 && !IsTransientError(result)) { - OnError(); - return; - } + HandleSendResult(result); // Send next packets if we have them waiting in the buffer. - while (!send_queue_.empty() && !send_pending_) { + while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) { DoSend(send_queue_.front()); - send_queue_bytes_ -= send_queue_.front().size; send_queue_.pop_front(); } } +void P2PSocketHostUdp::HandleSendResult(int result) { + if (result > 0) { + message_sender_->Send(new P2PMsg_OnSendComplete(id_)); + } else if (IsTransientError(result)) { + LOG(INFO) << "sendto() has failed twice returning a " + " transient error. Dropping the packet."; + } else if (result < 0) { + LOG(ERROR) << "Error when sending data in UDP socket: " << result; + OnError(); + } +} + P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection( const net::IPEndPoint& remote_address, int id) { NOTREACHED(); diff --git a/content/browser/renderer_host/p2p/socket_host_udp.h b/content/browser/renderer_host/p2p/socket_host_udp.h index 710f743..2b83c97 100644 --- a/content/browser/renderer_host/p2p/socket_host_udp.h +++ b/content/browser/renderer_host/p2p/socket_host_udp.h @@ -48,20 +48,20 @@ class CONTENT_EXPORT P2PSocketHostUdp : public P2PSocketHost { }; void OnError(); - void DoRead(); - void DoSend(const PendingPacket& packet); - void DidCompleteRead(int result); - // Callbacks for RecvFrom() and SendTo(). + void DoRead(); void OnRecv(int result); + void HandleReadResult(int result); + + void DoSend(const PendingPacket& packet); void OnSend(int result); + void HandleSendResult(int result); scoped_ptr<net::DatagramServerSocket> socket_; scoped_refptr<net::IOBuffer> recv_buffer_; net::IPEndPoint recv_address_; std::deque<PendingPacket> send_queue_; - int send_queue_bytes_; bool send_pending_; // Set of peer for which we have received STUN binding request or diff --git a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc index 5a5f15a..d7a3bec 100644 --- a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc +++ b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc @@ -164,6 +164,11 @@ class P2PSocketHostUdpTest : public testing::Test { // Verify that we can send STUN messages before we receive anything // from the other side. TEST_F(P2PSocketHostUdpTest, SendStunNoAuth) { + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .Times(3) + .WillRepeatedly(DoAll(DeleteArg<0>(), Return(true))); + std::vector<char> packet1; CreateStunRequest(&packet1); socket_host_->Send(dest1_, packet1); @@ -208,6 +213,9 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunRequest) { socket_->ReceivePacket(dest1_, request_packet); // Now we should be able to send any data to |dest1_|. + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); std::vector<char> packet; CreateRandomPacket(&packet); socket_host_->Send(dest1_, packet); @@ -228,6 +236,9 @@ TEST_F(P2PSocketHostUdpTest, SendAfterStunResponse) { socket_->ReceivePacket(dest1_, request_packet); // Now we should be able to send any data to |dest1_|. + EXPECT_CALL(sender_, Send( + MatchMessage(static_cast<uint32>(P2PMsg_OnSendComplete::ID)))) + .WillOnce(DoAll(DeleteArg<0>(), Return(true))); std::vector<char> packet; CreateRandomPacket(&packet); socket_host_->Send(dest1_, packet); diff --git a/content/common/p2p_messages.h b/content/common/p2p_messages.h index 59f6a8f..6b13831 100644 --- a/content/common/p2p_messages.h +++ b/content/common/p2p_messages.h @@ -35,6 +35,9 @@ IPC_MESSAGE_CONTROL2(P2PMsg_OnSocketCreated, int /* socket_id */, net::IPEndPoint /* socket_address */) +IPC_MESSAGE_CONTROL1(P2PMsg_OnSendComplete, + int /* socket_id */) + IPC_MESSAGE_CONTROL1(P2PMsg_OnError, int /* socket_id */) diff --git a/content/renderer/p2p/ipc_socket_factory.cc b/content/renderer/p2p/ipc_socket_factory.cc index ea53678..defe31f 100644 --- a/content/renderer/p2p/ipc_socket_factory.cc +++ b/content/renderer/p2p/ipc_socket_factory.cc @@ -16,6 +16,10 @@ namespace content { namespace { +// TODO(sergeyu): Try adjusting these parameters to achieve optimal performance. +const int kMaxPendingPackets = 8; +const int kWritableSignalThreshold = 0; + // IpcPacketSocket implements talk_base::AsyncPacketSocket interface // using P2PSocketClient that works over IPC-channel. It must be used // on the thread it was created. @@ -47,6 +51,7 @@ class IpcPacketSocket : public talk_base::AsyncPacketSocket, virtual void OnOpen(const net::IPEndPoint& address) OVERRIDE; virtual void OnIncomingTcpConnection(const net::IPEndPoint& address, P2PSocketClient* client) OVERRIDE; + virtual void OnSendComplete() OVERRIDE; virtual void OnError() OVERRIDE; virtual void OnDataReceived(const net::IPEndPoint& address, const std::vector<char>& data) OVERRIDE; @@ -83,6 +88,14 @@ class IpcPacketSocket : public talk_base::AsyncPacketSocket, // Current state of the object. InternalState state_; + // Number which have been sent to the browser, but for which we haven't + // received response. + int send_packets_pending_; + + // Set to true once EWOULDBLOCK was returned from Send(). Indicates that the + // caller expects SignalWritable notification. + bool writable_signal_expected_; + // Current error code. Valid when state_ == IS_ERROR. int error_; @@ -93,6 +106,8 @@ IpcPacketSocket::IpcPacketSocket() : type_(P2P_SOCKET_UDP), message_loop_(MessageLoop::current()), state_(IS_UNINITIALIZED), + send_packets_pending_(0), + writable_signal_expected_(false), error_(0) { } @@ -180,15 +195,22 @@ int IpcPacketSocket::SendTo(const void *data, size_t data_size, break; } + if (send_packets_pending_ > kMaxPendingPackets) { + writable_signal_expected_ = true; + error_ = EWOULDBLOCK; + return -1; + } + const char* data_char = reinterpret_cast<const char*>(data); std::vector<char> data_vector(data_char, data_char + data_size); net::IPEndPoint address_chrome; if (!jingle_glue::SocketAddressToIPEndPoint(address, &address_chrome)) { - // Just drop the packet if we failed to convert the address. - return 0; + NOTREACHED(); + return -1; } + ++send_packets_pending_; client_->Send(address_chrome, data_vector); // Fake successful send. The caller ignores result anyway. @@ -238,9 +260,6 @@ int IpcPacketSocket::GetOption(talk_base::Socket::Option opt, int* value) { int IpcPacketSocket::SetOption(talk_base::Socket::Option opt, int value) { // We don't support socket options for IPC sockets. - // - // TODO(sergeyu): Make sure we set proper socket options on the - // browser side. return -1; } @@ -287,6 +306,22 @@ void IpcPacketSocket::OnIncomingTcpConnection( SignalNewConnection(this, socket.release()); } +void IpcPacketSocket::OnSendComplete() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + --send_packets_pending_; + DCHECK_GE(send_packets_pending_, 0); + + if (writable_signal_expected_ && + send_packets_pending_ <= kWritableSignalThreshold) { + // TODO(sergeyu): Uncomment this line once SignalWritable is added in + // talk_base::AsyncPacketSocket. + // + // SignalWritable(this); + writable_signal_expected_ = false; + } +} + void IpcPacketSocket::OnError() { DCHECK_EQ(MessageLoop::current(), message_loop_); state_ = IS_ERROR; diff --git a/content/renderer/p2p/socket_client.cc b/content/renderer/p2p/socket_client.cc index e6f5a0f..ed3f211 100644 --- a/content/renderer/p2p/socket_client.cc +++ b/content/renderer/p2p/socket_client.cc @@ -134,6 +134,19 @@ void P2PSocketClient::DeliverOnIncomingTcpConnection( } } +void P2PSocketClient::OnSendComplete() { + DCHECK(ipc_message_loop_->BelongsToCurrentThread()); + + delegate_message_loop_->PostTask( + FROM_HERE, base::Bind(&P2PSocketClient::DeliverOnSendComplete, this)); +} + +void P2PSocketClient::DeliverOnSendComplete() { + DCHECK(delegate_message_loop_->BelongsToCurrentThread()); + if (delegate_) + delegate_->OnSendComplete(); +} + void P2PSocketClient::OnError() { DCHECK(ipc_message_loop_->BelongsToCurrentThread()); state_ = STATE_ERROR; diff --git a/content/renderer/p2p/socket_client.h b/content/renderer/p2p/socket_client.h index d74107f..fd8537e 100644 --- a/content/renderer/p2p/socket_client.h +++ b/content/renderer/p2p/socket_client.h @@ -27,7 +27,8 @@ class P2PSocketDispatcher; // thread which is specified in Init(). class P2PSocketClient : public base::RefCountedThreadSafe<P2PSocketClient> { public: - // Delegate is called on the the same thread on the delegate thread. + // Delegate is called on the the same thread on which P2PSocketCLient is + // created. class Delegate { public: virtual ~Delegate() { } @@ -35,6 +36,7 @@ class P2PSocketClient : public base::RefCountedThreadSafe<P2PSocketClient> { virtual void OnOpen(const net::IPEndPoint& address) = 0; virtual void OnIncomingTcpConnection(const net::IPEndPoint& address, P2PSocketClient* client) = 0; + virtual void OnSendComplete() = 0; virtual void OnError() = 0; virtual void OnDataReceived(const net::IPEndPoint& address, const std::vector<char>& data) = 0; @@ -80,6 +82,8 @@ class P2PSocketClient : public base::RefCountedThreadSafe<P2PSocketClient> { // Message handlers that run on IPC thread. void OnSocketCreated(const net::IPEndPoint& address); void OnIncomingTcpConnection(const net::IPEndPoint& address); + void OnSendComplete(int packet_id); + void OnSendComplete(); void OnError(); void OnDataReceived(const net::IPEndPoint& address, const std::vector<char>& data); @@ -89,6 +93,7 @@ class P2PSocketClient : public base::RefCountedThreadSafe<P2PSocketClient> { void DeliverOnIncomingTcpConnection( const net::IPEndPoint& address, scoped_refptr<P2PSocketClient> new_client); + void DeliverOnSendComplete(); void DeliverOnError(); void DeliverOnDataReceived(const net::IPEndPoint& address, const std::vector<char>& data); diff --git a/content/renderer/p2p/socket_dispatcher.cc b/content/renderer/p2p/socket_dispatcher.cc index c999e62..7fe71ee 100644 --- a/content/renderer/p2p/socket_dispatcher.cc +++ b/content/renderer/p2p/socket_dispatcher.cc @@ -63,6 +63,7 @@ bool P2PSocketDispatcher::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(P2PMsg_GetHostAddressResult, OnGetHostAddressResult) IPC_MESSAGE_HANDLER(P2PMsg_OnSocketCreated, OnSocketCreated) IPC_MESSAGE_HANDLER(P2PMsg_OnIncomingTcpConnection, OnIncomingTcpConnection) + IPC_MESSAGE_HANDLER(P2PMsg_OnSendComplete, OnSendComplete) IPC_MESSAGE_HANDLER(P2PMsg_OnError, OnError) IPC_MESSAGE_HANDLER(P2PMsg_OnDataReceived, OnDataReceived) IPC_MESSAGE_UNHANDLED(handled = false) @@ -152,6 +153,13 @@ void P2PSocketDispatcher::OnIncomingTcpConnection( } } +void P2PSocketDispatcher::OnSendComplete(int socket_id) { + P2PSocketClient* client = GetClient(socket_id); + if (client) { + client->OnSendComplete(); + } +} + void P2PSocketDispatcher::OnError(int socket_id) { P2PSocketClient* client = GetClient(socket_id); if (client) { diff --git a/content/renderer/p2p/socket_dispatcher.h b/content/renderer/p2p/socket_dispatcher.h index e17a8c1..cbd8f2a 100644 --- a/content/renderer/p2p/socket_dispatcher.h +++ b/content/renderer/p2p/socket_dispatcher.h @@ -102,6 +102,7 @@ class CONTENT_EXPORT P2PSocketDispatcher const net::IPAddressNumber& address); void OnSocketCreated(int socket_id, const net::IPEndPoint& address); void OnIncomingTcpConnection(int socket_id, const net::IPEndPoint& address); + void OnSendComplete(int socket_id); void OnError(int socket_id); void OnDataReceived(int socket_id, const net::IPEndPoint& address, const std::vector<char>& data); |