OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/p2p/base/p2ptransportchannel.h" | 11 #include "webrtc/p2p/base/p2ptransportchannel.h" |
12 | 12 |
13 #include <set> | 13 #include <set> |
14 #include <algorithm> | |
14 #include "webrtc/p2p/base/common.h" | 15 #include "webrtc/p2p/base/common.h" |
15 #include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE. | 16 #include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE. |
16 #include "webrtc/p2p/base/stunport.h" // For STUN_PORT_TYPE. | 17 #include "webrtc/p2p/base/stunport.h" // For STUN_PORT_TYPE. |
17 #include "webrtc/base/common.h" | 18 #include "webrtc/base/common.h" |
18 #include "webrtc/base/crc32.h" | 19 #include "webrtc/base/crc32.h" |
19 #include "webrtc/base/logging.h" | 20 #include "webrtc/base/logging.h" |
20 #include "webrtc/base/stringencode.h" | 21 #include "webrtc/base/stringencode.h" |
21 | 22 |
22 namespace { | 23 namespace { |
23 | 24 |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
173 return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; | 174 return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; |
174 } | 175 } |
175 | 176 |
176 } // unnamed namespace | 177 } // unnamed namespace |
177 | 178 |
178 namespace cricket { | 179 namespace cricket { |
179 | 180 |
180 P2PTransportChannel::P2PTransportChannel(const std::string& content_name, | 181 P2PTransportChannel::P2PTransportChannel(const std::string& content_name, |
181 int component, | 182 int component, |
182 P2PTransport* transport, | 183 P2PTransport* transport, |
183 PortAllocator *allocator) : | 184 PortAllocator* allocator) |
184 TransportChannelImpl(content_name, component), | 185 : TransportChannelImpl(content_name, component), |
185 transport_(transport), | 186 transport_(transport), |
186 allocator_(allocator), | 187 allocator_(allocator), |
187 worker_thread_(rtc::Thread::Current()), | 188 worker_thread_(rtc::Thread::Current()), |
188 incoming_only_(false), | 189 incoming_only_(false), |
189 waiting_for_signaling_(false), | 190 error_(0), |
190 error_(0), | 191 best_connection_(NULL), |
191 best_connection_(NULL), | 192 pending_best_connection_(NULL), |
192 pending_best_connection_(NULL), | 193 sort_dirty_(false), |
193 sort_dirty_(false), | 194 was_writable_(false), |
194 was_writable_(false), | 195 protocol_type_(ICEPROTO_HYBRID), |
195 protocol_type_(ICEPROTO_HYBRID), | 196 remote_ice_mode_(ICEMODE_FULL), |
196 remote_ice_mode_(ICEMODE_FULL), | 197 ice_role_(ICEROLE_UNKNOWN), |
197 ice_role_(ICEROLE_UNKNOWN), | 198 tiebreaker_(0), |
198 tiebreaker_(0), | 199 remote_candidate_generation_(0), |
199 remote_candidate_generation_(0), | 200 gathering_state_(kIceGatheringNew), |
200 check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), | 201 check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5), |
201 receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) { | 202 receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) { |
202 } | 203 } |
203 | 204 |
204 P2PTransportChannel::~P2PTransportChannel() { | 205 P2PTransportChannel::~P2PTransportChannel() { |
205 ASSERT(worker_thread_ == rtc::Thread::Current()); | 206 ASSERT(worker_thread_ == rtc::Thread::Current()); |
206 | 207 |
207 for (uint32 i = 0; i < allocator_sessions_.size(); ++i) | 208 for (uint32 i = 0; i < allocator_sessions_.size(); ++i) |
208 delete allocator_sessions_[i]; | 209 delete allocator_sessions_[i]; |
209 } | 210 } |
210 | 211 |
211 // Add the allocator session to our list so that we know which sessions | 212 // Add the allocator session to our list so that we know which sessions |
(...skipping 21 matching lines...) Expand all Loading... | |
233 connection->SignalReadPacket.connect( | 234 connection->SignalReadPacket.connect( |
234 this, &P2PTransportChannel::OnReadPacket); | 235 this, &P2PTransportChannel::OnReadPacket); |
235 connection->SignalReadyToSend.connect( | 236 connection->SignalReadyToSend.connect( |
236 this, &P2PTransportChannel::OnReadyToSend); | 237 this, &P2PTransportChannel::OnReadyToSend); |
237 connection->SignalStateChange.connect( | 238 connection->SignalStateChange.connect( |
238 this, &P2PTransportChannel::OnConnectionStateChange); | 239 this, &P2PTransportChannel::OnConnectionStateChange); |
239 connection->SignalDestroyed.connect( | 240 connection->SignalDestroyed.connect( |
240 this, &P2PTransportChannel::OnConnectionDestroyed); | 241 this, &P2PTransportChannel::OnConnectionDestroyed); |
241 connection->SignalUseCandidate.connect( | 242 connection->SignalUseCandidate.connect( |
242 this, &P2PTransportChannel::OnUseCandidate); | 243 this, &P2PTransportChannel::OnUseCandidate); |
244 had_connection_ = true; | |
243 } | 245 } |
244 | 246 |
245 void P2PTransportChannel::SetIceRole(IceRole ice_role) { | 247 void P2PTransportChannel::SetIceRole(IceRole ice_role) { |
246 ASSERT(worker_thread_ == rtc::Thread::Current()); | 248 ASSERT(worker_thread_ == rtc::Thread::Current()); |
247 if (ice_role_ != ice_role) { | 249 if (ice_role_ != ice_role) { |
248 ice_role_ = ice_role; | 250 ice_role_ = ice_role; |
249 for (std::vector<PortInterface *>::iterator it = ports_.begin(); | 251 for (std::vector<PortInterface *>::iterator it = ports_.begin(); |
250 it != ports_.end(); ++it) { | 252 it != ports_.end(); ++it) { |
251 (*it)->SetIceRole(ice_role); | 253 (*it)->SetIceRole(ice_role); |
252 } | 254 } |
(...skipping 14 matching lines...) Expand all Loading... | |
267 // Currently a channel is considered ICE completed once there is no | 269 // Currently a channel is considered ICE completed once there is no |
268 // more than one connection per Network. This works for a single NIC | 270 // more than one connection per Network. This works for a single NIC |
269 // with both IPv4 and IPv6 enabled. However, this condition won't | 271 // with both IPv4 and IPv6 enabled. However, this condition won't |
270 // happen when there are multiple NICs and all of them have | 272 // happen when there are multiple NICs and all of them have |
271 // connectivity. | 273 // connectivity. |
272 // TODO(guoweis): Change Completion to be driven by a channel level | 274 // TODO(guoweis): Change Completion to be driven by a channel level |
273 // timer. | 275 // timer. |
274 TransportChannelState P2PTransportChannel::GetState() const { | 276 TransportChannelState P2PTransportChannel::GetState() const { |
275 std::set<rtc::Network*> networks; | 277 std::set<rtc::Network*> networks; |
276 | 278 |
277 if (connections_.size() == 0) { | 279 if (connections_.size() == 0) { |
pthatcher1
2015/08/25 18:40:39
Could you change this to .empty() while you're her
Taylor Brandstetter
2015/08/25 20:39:54
Done.
| |
278 return TransportChannelState::STATE_FAILED; | 280 return had_connection_ ? TransportChannelState::STATE_FAILED |
281 : TransportChannelState::STATE_NEW; | |
279 } | 282 } |
280 | 283 |
281 for (uint32 i = 0; i < connections_.size(); ++i) { | 284 for (uint32 i = 0; i < connections_.size(); ++i) { |
282 rtc::Network* network = connections_[i]->port()->Network(); | 285 rtc::Network* network = connections_[i]->port()->Network(); |
283 if (networks.find(network) == networks.end()) { | 286 if (networks.find(network) == networks.end()) { |
284 networks.insert(network); | 287 networks.insert(network); |
285 } else { | 288 } else { |
286 LOG_J(LS_VERBOSE, this) << "Ice not completed yet for this channel as " | 289 LOG_J(LS_VERBOSE, this) << "Ice not completed yet for this channel as " |
287 << network->ToString() | 290 << network->ToString() |
288 << " has more than 1 connection."; | 291 << " has more than 1 connection."; |
(...skipping 29 matching lines...) Expand all Loading... | |
318 // ice ufrag or password. | 321 // ice ufrag or password. |
319 ice_restart = | 322 ice_restart = |
320 IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd); | 323 IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd); |
321 } | 324 } |
322 | 325 |
323 ice_ufrag_ = ice_ufrag; | 326 ice_ufrag_ = ice_ufrag; |
324 ice_pwd_ = ice_pwd; | 327 ice_pwd_ = ice_pwd; |
325 | 328 |
326 if (ice_restart) { | 329 if (ice_restart) { |
327 // Restart candidate gathering. | 330 // Restart candidate gathering. |
328 Allocate(); | 331 StartGatheringCandidates(); |
329 } | 332 } |
330 } | 333 } |
331 | 334 |
332 void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag, | 335 void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag, |
333 const std::string& ice_pwd) { | 336 const std::string& ice_pwd) { |
334 ASSERT(worker_thread_ == rtc::Thread::Current()); | 337 ASSERT(worker_thread_ == rtc::Thread::Current()); |
335 bool ice_restart = false; | 338 bool ice_restart = false; |
336 if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) { | 339 if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) { |
337 ice_restart = (remote_ice_ufrag_ != ice_ufrag) || | 340 ice_restart = (remote_ice_ufrag_ != ice_ufrag) || |
338 (remote_ice_pwd_!= ice_pwd); | 341 (remote_ice_pwd_!= ice_pwd); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
374 void P2PTransportChannel::Connect() { | 377 void P2PTransportChannel::Connect() { |
375 ASSERT(worker_thread_ == rtc::Thread::Current()); | 378 ASSERT(worker_thread_ == rtc::Thread::Current()); |
376 if (ice_ufrag_.empty() || ice_pwd_.empty()) { | 379 if (ice_ufrag_.empty() || ice_pwd_.empty()) { |
377 ASSERT(false); | 380 ASSERT(false); |
378 LOG(LS_ERROR) << "P2PTransportChannel::Connect: The ice_ufrag_ and the " | 381 LOG(LS_ERROR) << "P2PTransportChannel::Connect: The ice_ufrag_ and the " |
379 << "ice_pwd_ are not set."; | 382 << "ice_pwd_ are not set."; |
380 return; | 383 return; |
381 } | 384 } |
382 | 385 |
383 // Kick off an allocator session | 386 // Kick off an allocator session |
384 Allocate(); | 387 StartGatheringCandidates(); |
385 | 388 |
386 // Start pinging as the ports come in. | 389 // Start pinging as the ports come in. |
387 thread()->Post(this, MSG_PING); | 390 thread()->Post(this, MSG_PING); |
388 | 391 |
389 thread()->PostDelayed( | 392 thread()->PostDelayed( |
390 check_receiving_delay_, this, MSG_CHECK_RECEIVING); | 393 check_receiving_delay_, this, MSG_CHECK_RECEIVING); |
391 } | 394 } |
392 | 395 |
393 // A new port is available, attempt to make connections for it | 396 // A new port is available, attempt to make connections for it |
394 void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, | 397 void P2PTransportChannel::OnPortReady(PortAllocatorSession *session, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
428 for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); | 431 for (iter = remote_candidates_.begin(); iter != remote_candidates_.end(); |
429 ++iter) { | 432 ++iter) { |
430 CreateConnection(port, *iter, iter->origin_port(), false); | 433 CreateConnection(port, *iter, iter->origin_port(), false); |
431 } | 434 } |
432 | 435 |
433 SortConnections(); | 436 SortConnections(); |
434 } | 437 } |
435 | 438 |
436 // A new candidate is available, let listeners know | 439 // A new candidate is available, let listeners know |
437 void P2PTransportChannel::OnCandidatesReady( | 440 void P2PTransportChannel::OnCandidatesReady( |
438 PortAllocatorSession *session, const std::vector<Candidate>& candidates) { | 441 PortAllocatorSession* session, |
442 const std::vector<Candidate>& candidates) { | |
439 ASSERT(worker_thread_ == rtc::Thread::Current()); | 443 ASSERT(worker_thread_ == rtc::Thread::Current()); |
440 for (size_t i = 0; i < candidates.size(); ++i) { | 444 for (size_t i = 0; i < candidates.size(); ++i) { |
441 SignalCandidateReady(this, candidates[i]); | 445 SignalCandidateGathered(this, candidates[i]); |
442 } | 446 } |
443 } | 447 } |
444 | 448 |
445 void P2PTransportChannel::OnCandidatesAllocationDone( | 449 void P2PTransportChannel::OnCandidatesAllocationDone( |
446 PortAllocatorSession* session) { | 450 PortAllocatorSession* session) { |
447 ASSERT(worker_thread_ == rtc::Thread::Current()); | 451 ASSERT(worker_thread_ == rtc::Thread::Current()); |
448 SignalCandidatesAllocationDone(this); | 452 gathering_state_ = kIceGatheringComplete; |
453 LOG(LS_INFO) << "P2PTransportChannel: " << content_name() << ", component " | |
454 << component() << " gathering complete"; | |
455 SignalGatheringState(this); | |
449 } | 456 } |
450 | 457 |
451 // Handle stun packets | 458 // Handle stun packets |
452 void P2PTransportChannel::OnUnknownAddress( | 459 void P2PTransportChannel::OnUnknownAddress( |
453 PortInterface* port, | 460 PortInterface* port, |
454 const rtc::SocketAddress& address, ProtocolType proto, | 461 const rtc::SocketAddress& address, ProtocolType proto, |
455 IceMessage* stun_msg, const std::string &remote_username, | 462 IceMessage* stun_msg, const std::string &remote_username, |
456 bool port_muxed) { | 463 bool port_muxed) { |
457 ASSERT(worker_thread_ == rtc::Thread::Current()); | 464 ASSERT(worker_thread_ == rtc::Thread::Current()); |
458 | 465 |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
624 // connection in question. | 631 // connection in question. |
625 SortConnections(); | 632 SortConnections(); |
626 } | 633 } |
627 } | 634 } |
628 | 635 |
629 void P2PTransportChannel::OnRoleConflict(PortInterface* port) { | 636 void P2PTransportChannel::OnRoleConflict(PortInterface* port) { |
630 SignalRoleConflict(this); // STUN ping will be sent when SetRole is called | 637 SignalRoleConflict(this); // STUN ping will be sent when SetRole is called |
631 // from Transport. | 638 // from Transport. |
632 } | 639 } |
633 | 640 |
634 // When the signalling channel is ready, we can really kick off the allocator | |
635 void P2PTransportChannel::OnSignalingReady() { | |
636 ASSERT(worker_thread_ == rtc::Thread::Current()); | |
637 if (waiting_for_signaling_) { | |
638 waiting_for_signaling_ = false; | |
639 AddAllocatorSession(allocator_->CreateSession( | |
640 SessionId(), content_name(), component(), ice_ufrag_, ice_pwd_)); | |
641 } | |
642 } | |
643 | |
644 void P2PTransportChannel::OnUseCandidate(Connection* conn) { | 641 void P2PTransportChannel::OnUseCandidate(Connection* conn) { |
645 ASSERT(worker_thread_ == rtc::Thread::Current()); | 642 ASSERT(worker_thread_ == rtc::Thread::Current()); |
646 ASSERT(ice_role_ == ICEROLE_CONTROLLED); | 643 ASSERT(ice_role_ == ICEROLE_CONTROLLED); |
647 ASSERT(protocol_type_ == ICEPROTO_RFC5245); | 644 ASSERT(protocol_type_ == ICEPROTO_RFC5245); |
648 | 645 |
649 if (conn->write_state() == Connection::STATE_WRITABLE) { | 646 if (conn->write_state() == Connection::STATE_WRITABLE) { |
650 if (best_connection_ != conn) { | 647 if (best_connection_ != conn) { |
651 pending_best_connection_ = NULL; | 648 pending_best_connection_ = NULL; |
652 LOG(LS_INFO) << "Switching best connection on controlled side: " | 649 LOG(LS_INFO) << "Switching best connection on controlled side: " |
653 << conn->ToString(); | 650 << conn->ToString(); |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
910 return sent; | 907 return sent; |
911 } | 908 } |
912 | 909 |
913 bool P2PTransportChannel::GetStats(ConnectionInfos *infos) { | 910 bool P2PTransportChannel::GetStats(ConnectionInfos *infos) { |
914 ASSERT(worker_thread_ == rtc::Thread::Current()); | 911 ASSERT(worker_thread_ == rtc::Thread::Current()); |
915 // Gather connection infos. | 912 // Gather connection infos. |
916 infos->clear(); | 913 infos->clear(); |
917 | 914 |
918 std::vector<Connection *>::const_iterator it; | 915 std::vector<Connection *>::const_iterator it; |
919 for (it = connections_.begin(); it != connections_.end(); ++it) { | 916 for (it = connections_.begin(); it != connections_.end(); ++it) { |
920 Connection *connection = *it; | 917 Connection* connection = *it; |
921 ConnectionInfo info; | 918 ConnectionInfo info; |
922 info.best_connection = (best_connection_ == connection); | 919 info.best_connection = (best_connection_ == connection); |
923 info.readable = | 920 info.readable = |
924 (connection->read_state() == Connection::STATE_READABLE); | 921 (connection->read_state() == Connection::STATE_READABLE); |
925 info.writable = | 922 info.writable = |
926 (connection->write_state() == Connection::STATE_WRITABLE); | 923 (connection->write_state() == Connection::STATE_WRITABLE); |
927 info.timeout = | 924 info.timeout = |
928 (connection->write_state() == Connection::STATE_WRITE_TIMEOUT); | 925 (connection->write_state() == Connection::STATE_WRITE_TIMEOUT); |
929 info.new_connection = !connection->reported(); | 926 info.new_connection = !connection->reported(); |
930 connection->set_reported(true); | 927 connection->set_reported(true); |
(...skipping 14 matching lines...) Expand all Loading... | |
945 } | 942 } |
946 | 943 |
947 rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { | 944 rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { |
948 OptionMap::const_iterator it = options_.find(rtc::Socket::OPT_DSCP); | 945 OptionMap::const_iterator it = options_.find(rtc::Socket::OPT_DSCP); |
949 if (it == options_.end()) { | 946 if (it == options_.end()) { |
950 return rtc::DSCP_NO_CHANGE; | 947 return rtc::DSCP_NO_CHANGE; |
951 } | 948 } |
952 return static_cast<rtc::DiffServCodePoint> (it->second); | 949 return static_cast<rtc::DiffServCodePoint> (it->second); |
953 } | 950 } |
954 | 951 |
955 // Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending) | 952 void P2PTransportChannel::StartGatheringCandidates() { |
956 void P2PTransportChannel::Allocate() { | 953 // Time for a new allocator |
957 // Time for a new allocator, lets make sure we have a signalling channel | 954 if (gathering_state_ != kIceGatheringGathering) { |
958 // to communicate candidates through first. | 955 gathering_state_ = kIceGatheringGathering; |
959 waiting_for_signaling_ = true; | 956 SignalGatheringState(this); |
960 SignalRequestSignaling(this); | 957 } |
958 AddAllocatorSession(allocator_->CreateSession( | |
959 SessionId(), content_name(), component(), ice_ufrag_, ice_pwd_)); | |
961 } | 960 } |
962 | 961 |
963 // Monitor connection states. | 962 // Monitor connection states. |
964 void P2PTransportChannel::UpdateConnectionStates() { | 963 void P2PTransportChannel::UpdateConnectionStates() { |
965 uint32 now = rtc::Time(); | 964 uint32 now = rtc::Time(); |
966 | 965 |
967 // We need to copy the list of connections since some may delete themselves | 966 // We need to copy the list of connections since some may delete themselves |
968 // when we call UpdateState. | 967 // when we call UpdateState. |
969 for (uint32 i = 0; i < connections_.size(); ++i) | 968 for (uint32 i = 0; i < connections_.size(); ++i) |
970 connections_[i]->UpdateState(now); | 969 connections_[i]->UpdateState(now); |
(...skipping 428 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1399 std::vector<PortInterface*>::iterator iter = | 1398 std::vector<PortInterface*>::iterator iter = |
1400 std::find(ports_.begin(), ports_.end(), port); | 1399 std::find(ports_.begin(), ports_.end(), port); |
1401 if (iter != ports_.end()) | 1400 if (iter != ports_.end()) |
1402 ports_.erase(iter); | 1401 ports_.erase(iter); |
1403 | 1402 |
1404 LOG(INFO) << "Removed port from p2p socket: " | 1403 LOG(INFO) << "Removed port from p2p socket: " |
1405 << static_cast<int>(ports_.size()) << " remaining"; | 1404 << static_cast<int>(ports_.size()) << " remaining"; |
1406 } | 1405 } |
1407 | 1406 |
1408 // We data is available, let listeners know | 1407 // We data is available, let listeners know |
1409 void P2PTransportChannel::OnReadPacket( | 1408 void P2PTransportChannel::OnReadPacket(Connection* connection, |
1410 Connection *connection, const char *data, size_t len, | 1409 const char* data, |
1411 const rtc::PacketTime& packet_time) { | 1410 size_t len, |
1411 const rtc::PacketTime& packet_time) { | |
1412 ASSERT(worker_thread_ == rtc::Thread::Current()); | 1412 ASSERT(worker_thread_ == rtc::Thread::Current()); |
1413 | 1413 |
1414 // Do not deliver, if packet doesn't belong to the correct transport channel. | 1414 // Do not deliver, if packet doesn't belong to the correct transport channel. |
1415 if (!FindConnection(connection)) | 1415 if (!FindConnection(connection)) |
1416 return; | 1416 return; |
1417 | 1417 |
1418 // Let the client know of an incoming packet | 1418 // Let the client know of an incoming packet |
1419 SignalReadPacket(this, data, len, packet_time, 0); | 1419 SignalReadPacket(this, data, len, packet_time, 0); |
1420 } | 1420 } |
1421 | 1421 |
1422 void P2PTransportChannel::OnReadyToSend(Connection* connection) { | 1422 void P2PTransportChannel::OnReadyToSend(Connection* connection) { |
1423 if (connection == best_connection_ && writable()) { | 1423 if (connection == best_connection_ && writable()) { |
1424 SignalReadyToSend(this); | 1424 SignalReadyToSend(this); |
1425 } | 1425 } |
1426 } | 1426 } |
1427 | 1427 |
1428 } // namespace cricket | 1428 } // namespace cricket |
OLD | NEW |