CppServer  1.0.4.0
C++ Server Library
tcp_client.cpp
Go to the documentation of this file.
1 
10 
11 namespace CppServer {
12 namespace Asio {
13 
14 TCPClient::TCPClient(const std::shared_ptr<Service>& service, const std::string& address, int port)
15  : _id(CppCommon::UUID::Sequential()),
16  _service(service),
17  _io_service(_service->GetAsioService()),
18  _strand(*_io_service),
19  _strand_required(_service->IsStrandRequired()),
20  _address(address),
21  _port(port),
22  _socket(*_io_service),
23  _resolving(false),
24  _connecting(false),
25  _connected(false),
26  _bytes_pending(0),
27  _bytes_sending(0),
28  _bytes_sent(0),
29  _bytes_received(0),
30  _receiving(false),
31  _sending(false),
32  _send_buffer_flush_offset(0),
33  _option_keep_alive(false),
34  _option_no_delay(false)
35 {
36  assert((service != nullptr) && "Asio service is invalid!");
37  if (service == nullptr)
38  throw CppCommon::ArgumentException("Asio service is invalid!");
39 }
40 
41 TCPClient::TCPClient(const std::shared_ptr<Service>& service, const std::string& address, const std::string& scheme)
42  : _id(CppCommon::UUID::Sequential()),
43  _service(service),
44  _io_service(_service->GetAsioService()),
45  _strand(*_io_service),
46  _strand_required(_service->IsStrandRequired()),
47  _address(address),
48  _scheme(scheme),
49  _port(0),
50  _socket(*_io_service),
51  _resolving(false),
52  _connecting(false),
53  _connected(false),
54  _bytes_pending(0),
55  _bytes_sending(0),
56  _bytes_sent(0),
57  _bytes_received(0),
58  _receiving(false),
59  _sending(false),
60  _send_buffer_flush_offset(0),
61  _option_keep_alive(false),
62  _option_no_delay(false)
63 {
64  assert((service != nullptr) && "Asio service is invalid!");
65  if (service == nullptr)
66  throw CppCommon::ArgumentException("Asio service is invalid!");
67 }
68 
69 TCPClient::TCPClient(const std::shared_ptr<Service>& service, const asio::ip::tcp::endpoint& endpoint)
70  : _id(CppCommon::UUID::Sequential()),
71  _service(service),
72  _io_service(_service->GetAsioService()),
73  _strand(*_io_service),
74  _strand_required(_service->IsStrandRequired()),
75  _address(endpoint.address().to_string()),
76  _port(endpoint.port()),
77  _endpoint(endpoint),
78  _socket(*_io_service),
79  _resolving(false),
80  _connecting(false),
81  _connected(false),
82  _bytes_pending(0),
83  _bytes_sending(0),
84  _bytes_sent(0),
85  _bytes_received(0),
86  _receiving(false),
87  _sending(false),
88  _send_buffer_flush_offset(0),
89  _option_keep_alive(false),
90  _option_no_delay(false)
91 {
92  assert((service != nullptr) && "Asio service is invalid!");
93  if (service == nullptr)
94  throw CppCommon::ArgumentException("Asio service is invalid!");
95 }
96 
98 {
99  asio::socket_base::receive_buffer_size option;
100  _socket.get_option(option);
101  return option.value();
102 }
103 
105 {
106  asio::socket_base::send_buffer_size option;
107  _socket.get_option(option);
108  return option.value();
109 }
110 
112 {
113  asio::socket_base::receive_buffer_size option((int)size);
114  _socket.set_option(option);
115 }
116 
118 {
119  asio::socket_base::send_buffer_size option((int)size);
120  _socket.set_option(option);
121 }
122 
124 {
125  if (IsConnected())
126  return false;
127 
128  asio::error_code ec;
129 
130  // Create the server endpoint
131  _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (unsigned short)_port);
132 
133  // Connect to the server
134  _socket.connect(_endpoint, ec);
135 
136  // Disconnect on error
137  if (ec)
138  {
139  SendError(ec);
140 
141  // Call the client disconnected handler
142  onDisconnected();
143 
144  return false;
145  }
146 
147  // Apply the option: keep alive
148  if (option_keep_alive())
149  _socket.set_option(asio::ip::tcp::socket::keep_alive(true));
150  // Apply the option: no delay
151  if (option_no_delay())
152  _socket.set_option(asio::ip::tcp::no_delay(true));
153 
154  // Prepare receive & send buffers
155  _receive_buffer.resize(option_receive_buffer_size());
156  _send_buffer_main.reserve(option_send_buffer_size());
157  _send_buffer_flush.reserve(option_send_buffer_size());
158 
159  // Reset statistic
160  _bytes_pending = 0;
161  _bytes_sending = 0;
162  _bytes_sent = 0;
163  _bytes_received = 0;
164 
165  // Update the connected flag
166  _connected = true;
167 
168  // Call the client connected handler
169  onConnected();
170 
171  // Call the empty send buffer handler
172  if (_send_buffer_main.empty())
173  onEmpty();
174 
175  return true;
176 }
177 
178 bool TCPClient::Connect(const std::shared_ptr<TCPResolver>& resolver)
179 {
180  if (IsConnected())
181  return false;
182 
183  asio::error_code ec;
184 
185  // Resolve the server endpoint
186  asio::ip::tcp::resolver::query query(_address, (_scheme.empty() ? std::to_string(_port) : _scheme));
187  auto endpoints = resolver->resolver().resolve(query, ec);
188 
189  // Disconnect on error
190  if (ec)
191  {
192  SendError(ec);
193 
194  // Call the client disconnected handler
195  onDisconnected();
196 
197  return false;
198  }
199 
200  // Connect to the server
201  _endpoint = asio::connect(_socket, endpoints, ec);
202 
203  // Disconnect on error
204  if (ec)
205  {
206  SendError(ec);
207 
208  // Call the client disconnected handler
209  onDisconnected();
210 
211  return false;
212  }
213 
214  // Apply the option: keep alive
215  if (option_keep_alive())
216  _socket.set_option(asio::ip::tcp::socket::keep_alive(true));
217  // Apply the option: no delay
218  if (option_no_delay())
219  _socket.set_option(asio::ip::tcp::no_delay(true));
220 
221  // Prepare receive & send buffers
222  _receive_buffer.resize(option_receive_buffer_size());
223  _send_buffer_main.reserve(option_send_buffer_size());
224  _send_buffer_flush.reserve(option_send_buffer_size());
225 
226  // Reset statistic
227  _bytes_pending = 0;
228  _bytes_sending = 0;
229  _bytes_sent = 0;
230  _bytes_received = 0;
231 
232  // Update the connected flag
233  _connected = true;
234 
235  // Call the client connected handler
236  onConnected();
237 
238  // Call the empty send buffer handler
239  if (_send_buffer_main.empty())
240  onEmpty();
241 
242  return true;
243 }
244 
245 bool TCPClient::DisconnectInternal()
246 {
247  if (!IsConnected())
248  return false;
249 
250  // Close the client socket
251  _socket.close();
252 
253  // Update the connected flag
254  _resolving = false;
255  _connecting = false;
256  _connected = false;
257 
258  // Update sending/receiving flags
259  _receiving = false;
260  _sending = false;
261 
262  // Clear send/receive buffers
263  ClearBuffers();
264 
265  // Call the client disconnected handler
266  onDisconnected();
267 
268  return true;
269 }
270 
272 {
273  if (!Disconnect())
274  return false;
275 
276  return Connect();
277 }
278 
280 {
281  if (IsConnected() || _resolving || _connecting)
282  return false;
283 
284  // Post the connect handler
285  auto self(this->shared_from_this());
286  auto connect_handler = [this, self]()
287  {
288  if (IsConnected() || _resolving || _connecting)
289  return;
290 
291  // Async connect with the connect handler
292  _connecting = true;
293  auto async_connect_handler = [this, self](std::error_code ec)
294  {
295  _connecting = false;
296 
297  if (IsConnected() || _resolving || _connecting)
298  return;
299 
300  if (!ec)
301  {
302  // Apply the option: keep alive
303  if (option_keep_alive())
304  _socket.set_option(asio::ip::tcp::socket::keep_alive(true));
305  // Apply the option: no delay
306  if (option_no_delay())
307  _socket.set_option(asio::ip::tcp::no_delay(true));
308 
309  // Prepare receive & send buffers
310  _receive_buffer.resize(option_receive_buffer_size());
311  _send_buffer_main.reserve(option_send_buffer_size());
312  _send_buffer_flush.reserve(option_send_buffer_size());
313 
314  // Reset statistic
315  _bytes_pending = 0;
316  _bytes_sending = 0;
317  _bytes_sent = 0;
318  _bytes_received = 0;
319 
320  // Update the connected flag
321  _connected = true;
322 
323  // Try to receive something from the server
324  TryReceive();
325 
326  // Call the client connected handler
327  onConnected();
328 
329  // Call the empty send buffer handler
330  if (_send_buffer_main.empty())
331  onEmpty();
332  }
333  else
334  {
335  SendError(ec);
336 
337  // Call the client disconnected handler
338  onDisconnected();
339  }
340  };
341 
342  // Create the server endpoint
343  _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (unsigned short)_port);
344 
345  if (_strand_required)
346  _socket.async_connect(_endpoint, bind_executor(_strand, async_connect_handler));
347  else
348  _socket.async_connect(_endpoint, async_connect_handler);
349  };
350  if (_strand_required)
351  _strand.post(connect_handler);
352  else
353  _io_service->post(connect_handler);
354 
355  return true;
356 }
357 
358 bool TCPClient::ConnectAsync(const std::shared_ptr<TCPResolver>& resolver)
359 {
360  if (IsConnected() || _resolving || _connecting)
361  return false;
362 
363  // Post the connect handler
364  auto self(this->shared_from_this());
365  auto connect_handler = [this, self, resolver]()
366  {
367  if (IsConnected() || _resolving || _connecting)
368  return;
369 
370  // Async resolve with the connect handler
371  _resolving = true;
372  auto async_resolve_handler = [this, self](std::error_code ec1, asio::ip::tcp::resolver::results_type endpoints)
373  {
374  _resolving = false;
375 
376  if (IsConnected() || _resolving || _connecting)
377  return;
378 
379  if (!ec1)
380  {
381  // Async connect with the connect handler
382  _connecting = true;
383  auto async_connect_handler = [this, self](std::error_code ec2, const asio::ip::tcp::endpoint& endpoint)
384  {
385  _connecting = false;
386 
387  if (IsConnected() || _resolving || _connecting)
388  return;
389 
390  if (!ec2)
391  {
392  // Connect to the server
393  _endpoint = endpoint;
394 
395  // Apply the option: keep alive
396  if (option_keep_alive())
397  _socket.set_option(asio::ip::tcp::socket::keep_alive(true));
398  // Apply the option: no delay
399  if (option_no_delay())
400  _socket.set_option(asio::ip::tcp::no_delay(true));
401 
402  // Prepare receive & send buffers
403  _receive_buffer.resize(option_receive_buffer_size());
404  _send_buffer_main.reserve(option_send_buffer_size());
405  _send_buffer_flush.reserve(option_send_buffer_size());
406 
407  // Reset statistic
408  _bytes_pending = 0;
409  _bytes_sending = 0;
410  _bytes_sent = 0;
411  _bytes_received = 0;
412 
413  // Update the connected flag
414  _connected = true;
415 
416  // Try to receive something from the server
417  TryReceive();
418 
419  // Call the client connected handler
420  onConnected();
421 
422  // Call the empty send buffer handler
423  if (_send_buffer_main.empty())
424  onEmpty();
425  }
426  else
427  {
428  SendError(ec2);
429 
430  // Call the client disconnected handler
431  onDisconnected();
432  }
433  };
434  if (_strand_required)
435  asio::async_connect(_socket, endpoints, bind_executor(_strand, async_connect_handler));
436  else
437  asio::async_connect(_socket, endpoints, async_connect_handler);
438  }
439  else
440  {
441  SendError(ec1);
442 
443  // Call the client disconnected handler
444  onDisconnected();
445  }
446  };
447 
448  // Resolve the server endpoint
449  asio::ip::tcp::resolver::query query(_address, (_scheme.empty() ? std::to_string(_port) : _scheme));
450  if (_strand_required)
451  resolver->resolver().async_resolve(query, bind_executor(_strand, async_resolve_handler));
452  else
453  resolver->resolver().async_resolve(query, async_resolve_handler);
454  };
455  if (_strand_required)
456  _strand.post(connect_handler);
457  else
458  _io_service->post(connect_handler);
459 
460  return true;
461 }
462 
463 bool TCPClient::DisconnectInternalAsync(bool dispatch)
464 {
465  if (!IsConnected() || _resolving || _connecting)
466  return false;
467 
468  asio::error_code ec;
469 
470  // Cancel the client socket
471  _socket.cancel(ec);
472 
473  // Dispatch or post the disconnect handler
474  auto self(this->shared_from_this());
475  auto disconnect_handler = [this, self]() { DisconnectInternal(); };
476  if (_strand_required)
477  {
478  if (dispatch)
479  _strand.dispatch(disconnect_handler);
480  else
481  _strand.post(disconnect_handler);
482  }
483  else
484  {
485  if (dispatch)
486  _io_service->dispatch(disconnect_handler);
487  else
488  _io_service->post(disconnect_handler);
489  }
490 
491  return true;
492 }
493 
495 {
496  if (!DisconnectAsync())
497  return false;
498 
499  while (IsConnected())
500  CppCommon::Thread::Yield();
501 
502  return ConnectAsync();
503 }
504 
505 size_t TCPClient::Send(const void* buffer, size_t size)
506 {
507  if (!IsConnected())
508  return 0;
509 
510  if (size == 0)
511  return 0;
512 
513  assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
514  if (buffer == nullptr)
515  return 0;
516 
517  asio::error_code ec;
518 
519  // Send data to the server
520  size_t sent = asio::write(_socket, asio::buffer(buffer, size), ec);
521  if (sent > 0)
522  {
523  // Update statistic
524  _bytes_sent += sent;
525 
526  // Call the buffer sent handler
527  onSent(sent, bytes_pending());
528  }
529 
530  // Disconnect on error
531  if (ec)
532  {
533  SendError(ec);
534  Disconnect();
535  }
536 
537  return sent;
538 }
539 
540 size_t TCPClient::Send(const void* buffer, size_t size, const CppCommon::Timespan& timeout)
541 {
542  if (!IsConnected())
543  return 0;
544 
545  if (size == 0)
546  return 0;
547 
548  assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
549  if (buffer == nullptr)
550  return 0;
551 
552  int done = 0;
553  std::mutex mtx;
554  std::condition_variable cv;
555  asio::error_code error;
556  asio::system_timer timer(_socket.get_executor());
557 
558  // Prepare done handler
559  auto async_done_handler = [&](asio::error_code ec)
560  {
561  std::unique_lock<std::mutex> lck(mtx);
562  if (done++ == 0)
563  {
564  error = ec;
565  _socket.cancel();
566  timer.cancel();
567  }
568  cv.notify_one();
569  };
570 
571  // Async wait for timeout
572  timer.expires_from_now(timeout.chrono());
573  timer.async_wait([&](const asio::error_code& ec) { async_done_handler(ec ? ec : asio::error::timed_out); });
574 
575  // Async write some data to the server
576  size_t sent = 0;
577  _socket.async_write_some(asio::buffer(buffer, size), [&](std::error_code ec, size_t write) { async_done_handler(ec); sent = write; });
578 
579  // Wait for complete or timeout
580  std::unique_lock<std::mutex> lck(mtx);
581  cv.wait(lck, [&]() { return done == 2; });
582 
583  // Send data to the server
584  if (sent > 0)
585  {
586  // Update statistic
587  _bytes_sent += sent;
588 
589  // Call the buffer sent handler
590  onSent(sent, bytes_pending());
591  }
592 
593  // Disconnect on error
594  if (error && (error != asio::error::timed_out))
595  {
596  SendError(error);
597  Disconnect();
598  }
599 
600  return sent;
601 }
602 
603 bool TCPClient::SendAsync(const void* buffer, size_t size)
604 {
605  if (!IsConnected())
606  return false;
607 
608  if (size == 0)
609  return true;
610 
611  assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
612  if (buffer == nullptr)
613  return false;
614 
615  {
616  std::scoped_lock locker(_send_lock);
617 
618  // Detect multiple send handlers
619  bool send_required = _send_buffer_main.empty() || _send_buffer_flush.empty();
620 
621  // Check the send buffer limit
622  if (((_send_buffer_main.size() + size) > _send_buffer_limit) && (_send_buffer_limit > 0))
623  {
624  SendError(asio::error::no_buffer_space);
625  return false;
626  }
627 
628  // Fill the main send buffer
629  const uint8_t* bytes = (const uint8_t*)buffer;
630  _send_buffer_main.insert(_send_buffer_main.end(), bytes, bytes + size);
631 
632  // Update statistic
633  _bytes_pending = _send_buffer_main.size();
634 
635  // Avoid multiple send handlers
636  if (!send_required)
637  return true;
638  }
639 
640  // Dispatch the send handler
641  auto self(this->shared_from_this());
642  auto send_handler = [this, self]()
643  {
644  // Try to send the main buffer
645  TrySend();
646  };
647  if (_strand_required)
648  _strand.dispatch(send_handler);
649  else
650  _io_service->dispatch(send_handler);
651 
652  return true;
653 }
654 
655 size_t TCPClient::Receive(void* buffer, size_t size)
656 {
657  if (!IsConnected())
658  return 0;
659 
660  if (size == 0)
661  return 0;
662 
663  assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
664  if (buffer == nullptr)
665  return 0;
666 
667  asio::error_code ec;
668 
669  // Receive data from the server
670  size_t received = _socket.read_some(asio::buffer(buffer, size), ec);
671  if (received > 0)
672  {
673  // Update statistic
674  _bytes_received += received;
675 
676  // Call the buffer received handler
677  onReceived(buffer, received);
678  }
679 
680  // Disconnect on error
681  if (ec)
682  {
683  SendError(ec);
684  Disconnect();
685  }
686 
687  return received;
688 }
689 
690 std::string TCPClient::Receive(size_t size)
691 {
692  std::string text(size, 0);
693  text.resize(Receive(text.data(), text.size()));
694  return text;
695 }
696 
697 size_t TCPClient::Receive(void* buffer, size_t size, const CppCommon::Timespan& timeout)
698 {
699  if (!IsConnected())
700  return 0;
701 
702  if (size == 0)
703  return 0;
704 
705  assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
706  if (buffer == nullptr)
707  return 0;
708 
709  int done = 0;
710  std::mutex mtx;
711  std::condition_variable cv;
712  asio::error_code error;
713  asio::system_timer timer(_socket.get_executor());
714 
715  // Prepare done handler
716  auto async_done_handler = [&](asio::error_code ec)
717  {
718  std::unique_lock<std::mutex> lck(mtx);
719  if (done++ == 0)
720  {
721  error = ec;
722  _socket.cancel();
723  timer.cancel();
724  }
725  cv.notify_one();
726  };
727 
728  // Async wait for timeout
729  timer.expires_from_now(timeout.chrono());
730  timer.async_wait([&](const asio::error_code& ec) { async_done_handler(ec ? ec : asio::error::timed_out); });
731 
732  // Async read some data from the server
733  size_t received = 0;
734  _socket.async_read_some(asio::buffer(buffer, size), [&](std::error_code ec, size_t read) { async_done_handler(ec); received = read; });
735 
736  // Wait for complete or timeout
737  std::unique_lock<std::mutex> lck(mtx);
738  cv.wait(lck, [&]() { return done == 2; });
739 
740  // Received some data from the server
741  if (received > 0)
742  {
743  // Update statistic
744  _bytes_received += received;
745 
746  // Call the buffer received handler
747  onReceived(buffer, received);
748  }
749 
750  // Disconnect on error
751  if (error && (error != asio::error::timed_out))
752  {
753  SendError(error);
754  Disconnect();
755  }
756 
757  return received;
758 }
759 
760 std::string TCPClient::Receive(size_t size, const CppCommon::Timespan& timeout)
761 {
762  std::string text(size, 0);
763  text.resize(Receive(text.data(), text.size(), timeout));
764  return text;
765 }
766 
768 {
769  // Try to receive data from the server
770  TryReceive();
771 }
772 
773 void TCPClient::TryReceive()
774 {
775  if (_receiving)
776  return;
777 
778  if (!IsConnected())
779  return;
780 
781  // Async receive with the receive handler
782  _receiving = true;
783  auto self(this->shared_from_this());
784  auto async_receive_handler = make_alloc_handler(_receive_storage, [this, self](std::error_code ec, size_t size)
785  {
786  _receiving = false;
787 
788  if (!IsConnected())
789  return;
790 
791  // Received some data from the server
792  if (size > 0)
793  {
794  // Update statistic
795  _bytes_received += size;
796 
797  // Call the buffer received handler
798  onReceived(_receive_buffer.data(), size);
799 
800  // If the receive buffer is full increase its size
801  if (_receive_buffer.size() == size)
802  {
803  // Check the receive buffer limit
804  if (((2 * size) > _receive_buffer_limit) && (_receive_buffer_limit > 0))
805  {
806  SendError(asio::error::no_buffer_space);
807  DisconnectInternalAsync(true);
808  return;
809  }
810 
811  _receive_buffer.resize(2 * size);
812  }
813  }
814 
815  // Try to receive again if the session is valid
816  if (!ec)
817  TryReceive();
818  else
819  {
820  SendError(ec);
821  DisconnectInternalAsync(true);
822  }
823  });
824  if (_strand_required)
825  _socket.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), bind_executor(_strand, async_receive_handler));
826  else
827  _socket.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), async_receive_handler);
828 }
829 
830 void TCPClient::TrySend()
831 {
832  if (_sending)
833  return;
834 
835  if (!IsConnected())
836  return;
837 
838  // Swap send buffers
839  if (_send_buffer_flush.empty())
840  {
841  std::scoped_lock locker(_send_lock);
842 
843  // Swap flush and main buffers
844  _send_buffer_flush.swap(_send_buffer_main);
845  _send_buffer_flush_offset = 0;
846 
847  // Update statistic
848  _bytes_pending = 0;
849  _bytes_sending += _send_buffer_flush.size();
850  }
851 
852  // Check if the flush buffer is empty
853  if (_send_buffer_flush.empty())
854  {
855  // Call the empty send buffer handler
856  onEmpty();
857  return;
858  }
859 
860  // Async write with the write handler
861  _sending = true;
862  auto self(this->shared_from_this());
863  auto async_write_handler = make_alloc_handler(_send_storage, [this, self](std::error_code ec, size_t size)
864  {
865  _sending = false;
866 
867  if (!IsConnected())
868  return;
869 
870  // Send some data to the server
871  if (size > 0)
872  {
873  // Update statistic
874  _bytes_sending -= size;
875  _bytes_sent += size;
876 
877  // Increase the flush buffer offset
878  _send_buffer_flush_offset += size;
879 
880  // Successfully send the whole flush buffer
881  if (_send_buffer_flush_offset == _send_buffer_flush.size())
882  {
883  // Clear the flush buffer
884  _send_buffer_flush.clear();
885  _send_buffer_flush_offset = 0;
886  }
887 
888  // Call the buffer sent handler
889  onSent(size, bytes_pending());
890  }
891 
892  // Try to send again if the session is valid
893  if (!ec)
894  TrySend();
895  else
896  {
897  SendError(ec);
898  DisconnectInternalAsync(true);
899  }
900  });
901  if (_strand_required)
902  _socket.async_write_some(asio::buffer(_send_buffer_flush.data() + _send_buffer_flush_offset, _send_buffer_flush.size() - _send_buffer_flush_offset), bind_executor(_strand, async_write_handler));
903  else
904  _socket.async_write_some(asio::buffer(_send_buffer_flush.data() + _send_buffer_flush_offset, _send_buffer_flush.size() - _send_buffer_flush_offset), async_write_handler);
905 }
906 
907 void TCPClient::ClearBuffers()
908 {
909  {
910  std::scoped_lock locker(_send_lock);
911 
912  // Clear send buffers
913  _send_buffer_main.clear();
914  _send_buffer_flush.clear();
915  _send_buffer_flush_offset = 0;
916 
917  // Update statistic
918  _bytes_pending = 0;
919  _bytes_sending = 0;
920  }
921 }
922 
923 void TCPClient::SendError(std::error_code ec)
924 {
925  // Skip Asio disconnect errors
926  if ((ec == asio::error::connection_aborted) ||
927  (ec == asio::error::connection_refused) ||
928  (ec == asio::error::connection_reset) ||
929  (ec == asio::error::eof) ||
930  (ec == asio::error::operation_aborted))
931  return;
932 
933  onError(ec.value(), ec.category().name(), ec.message());
934 }
935 
936 } // namespace Asio
937 } // namespace CppServer
size_t option_send_buffer_size() const
Get the option: send buffer size.
Definition: tcp_client.cpp:104
virtual size_t Receive(void *buffer, size_t size)
Receive data from the server (synchronous)
Definition: tcp_client.cpp:655
bool IsConnected() const noexcept
Is the client connected?
Definition: tcp_client.h:101
virtual bool Connect()
Connect the client (synchronous)
Definition: tcp_client.cpp:123
virtual size_t Send(const void *buffer, size_t size)
Send data to the server (synchronous)
Definition: tcp_client.cpp:505
TCPClient(const std::shared_ptr< Service > &service, const std::string &address, int port)
Initialize TCP client with a given Asio service, server address and port number.
Definition: tcp_client.cpp:14
virtual bool Disconnect()
Disconnect the client (synchronous)
Definition: tcp_client.h:124
virtual bool SendAsync(const void *buffer, size_t size)
Send data to the server (asynchronous)
Definition: tcp_client.cpp:603
virtual void onSent(size_t sent, size_t pending)
Handle buffer sent notification.
Definition: tcp_client.h:303
virtual bool ReconnectAsync()
Reconnect the client (asynchronous)
Definition: tcp_client.cpp:494
virtual void onReceived(const void *buffer, size_t size)
Handle buffer received notification.
Definition: tcp_client.h:291
virtual void onDisconnected()
Handle client disconnected notification.
Definition: tcp_client.h:281
virtual void onConnected()
Handle client connected notification.
Definition: tcp_client.h:279
void SetupReceiveBufferSize(size_t size)
Setup option: receive buffer size.
Definition: tcp_client.cpp:111
virtual void onEmpty()
Handle empty send buffer notification.
Definition: tcp_client.h:312
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
Definition: tcp_client.h:320
std::shared_ptr< Service > & service() noexcept
Get the Asio service.
Definition: tcp_client.h:63
asio::ip::tcp::endpoint & endpoint() noexcept
Get the client endpoint.
Definition: tcp_client.h:69
uint64_t bytes_pending() const noexcept
Get the number of bytes pending sent by the client.
Definition: tcp_client.h:81
virtual bool ConnectAsync()
Connect the client (asynchronous)
Definition: tcp_client.cpp:279
bool option_no_delay() const noexcept
Get the option: no delay.
Definition: tcp_client.h:90
virtual bool DisconnectAsync()
Disconnect the client (asynchronous)
Definition: tcp_client.h:146
bool option_keep_alive() const noexcept
Get the option: keep alive.
Definition: tcp_client.h:88
virtual void ReceiveAsync()
Receive data from the server (asynchronous)
Definition: tcp_client.cpp:767
void SetupSendBufferSize(size_t size)
Setup option: send buffer size.
Definition: tcp_client.cpp:117
size_t option_receive_buffer_size() const
Get the option: receive buffer size.
Definition: tcp_client.cpp:97
virtual bool Reconnect()
Reconnect the client (synchronous)
Definition: tcp_client.cpp:271
AllocateHandler< THandler > make_alloc_handler(HandlerStorage &storage, THandler handler)
Helper function to wrap a handler object to add custom allocation.
Definition: memory.inl:39
C++ Server project definitions.
Definition: asio.h:56
TCP client definition.