Add basic socket library
This commit is contained in:
parent
942f838774
commit
bd7190308f
|
@ -0,0 +1,11 @@
|
|||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.VC.opendb
|
||||
*.VC.db
|
||||
|
||||
build
|
||||
bin
|
||||
lib
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
|
@ -0,0 +1,16 @@
|
|||
cmake_minimum_required (VERSION 2.6)
|
||||
project (CarlaServer)
|
||||
|
||||
# Boost configuration
|
||||
SET(Boost_USE_STATIC_LIBS ON)
|
||||
find_package(Boost REQUIRED system date_time regex)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/source")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../Common")
|
||||
|
||||
add_subdirectory(source/carla/server)
|
||||
add_subdirectory(source/test)
|
|
@ -0,0 +1,10 @@
|
|||
BUILD_FOLDER=build
|
||||
|
||||
vsproject:
|
||||
cmake -H. -B$(BUILD_FOLDER) -G "Visual Studio 14 2015 Win64"
|
||||
|
||||
clean:
|
||||
rm -Rf build CMakeFiles
|
||||
|
||||
clean-all: clean
|
||||
rm -Rf bin lib
|
|
@ -0,0 +1,15 @@
|
|||
CarlaServer
|
||||
===========
|
||||
|
||||
Library for socket communications.
|
||||
|
||||
Requires boost libraries installed.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
To generate the Visual Studio solution
|
||||
|
||||
make vsproject
|
||||
|
||||
The solution gets generated at `./build/CarlaServer.sln`.
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#define CARLA_API
|
||||
|
||||
#include "../../Common/NonCopyable.h"
|
|
@ -0,0 +1,12 @@
|
|||
# add_library(carla_server
|
||||
# CarlaServer.h
|
||||
# CarlaServer.cpp
|
||||
# TCPServer.h
|
||||
# TCPServer.cpp
|
||||
# )
|
||||
file(GLOB carla_server_SRC
|
||||
"*.h"
|
||||
"*.cpp"
|
||||
)
|
||||
|
||||
add_library(carla_server ${carla_server_SRC})
|
|
@ -0,0 +1,89 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#include "Carla.h"
|
||||
#include "CarlaServer.h"
|
||||
|
||||
#include <carla/server/TCPServer.h>
|
||||
#include <carla/thread/AsyncReaderJobQueue.h>
|
||||
#include <carla/thread/AsyncWriterJobQueue.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace carla {
|
||||
namespace server {
|
||||
|
||||
template <typename ERROR_CODE>
|
||||
static void logTCPError(const std::string &text, const ERROR_CODE &errorCode) {
|
||||
std::cerr << "CarlaServer - TCP Server: " << text << ": " << errorCode.message() << std::endl;
|
||||
}
|
||||
|
||||
// -- Static methods ---------------------------------------------------------
|
||||
|
||||
// This is the thread that sends a string over the TCP socket.
|
||||
static void serverWorkerThread(TCPServer &server, const std::string &message) {
|
||||
TCPServer::error_code error;
|
||||
server.writeString(message, error);
|
||||
if (error)
|
||||
logTCPError("Failed to send", error);
|
||||
}
|
||||
|
||||
// This is the thread that listens for string over the TCP socket.
|
||||
static std::string clientWorkerThread(TCPServer &server) {
|
||||
std::string message;
|
||||
TCPServer::error_code error;
|
||||
server.readString(message, error);
|
||||
if (error && (error != boost::asio::error::eof)) { // eof is expected.
|
||||
logTCPError("Failed to read", error);
|
||||
return std::string();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
// -- CarlaServer::Pimpl -----------------------------------------------------
|
||||
|
||||
class CarlaServer::Pimpl : private NonCopyable {
|
||||
public:
|
||||
|
||||
explicit Pimpl(int writePort, int readPort) :
|
||||
_server(writePort),
|
||||
_client(readPort),
|
||||
_serverThread([this](const std::string &str){ serverWorkerThread(this->_server, str); }),
|
||||
_clientThread([this](){ return clientWorkerThread(this->_client); }) {}
|
||||
|
||||
void writeString(const std::string &message) {
|
||||
_serverThread.push(message);
|
||||
}
|
||||
|
||||
bool tryReadString(std::string &message) {
|
||||
return _clientThread.tryPop(message);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TCPServer _server;
|
||||
|
||||
TCPServer _client;
|
||||
|
||||
thread::AsyncReaderJobQueue<std::string> _serverThread;
|
||||
|
||||
thread::AsyncWriterJobQueue<std::string> _clientThread;
|
||||
};
|
||||
|
||||
// -- CarlaServer ------------------------------------------------------------
|
||||
|
||||
CarlaServer::CarlaServer(int writePort, int readPort) :
|
||||
_pimpl(std::make_unique<Pimpl>(writePort, readPort)) {}
|
||||
|
||||
CarlaServer::~CarlaServer() {}
|
||||
|
||||
void CarlaServer::writeString(const std::string &message) {
|
||||
_pimpl->writeString(message);
|
||||
}
|
||||
|
||||
bool CarlaServer::tryReadString(std::string &message) {
|
||||
return _pimpl->tryReadString(message);
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace carla
|
|
@ -0,0 +1,41 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace carla {
|
||||
namespace server {
|
||||
|
||||
/// Asynchronous TCP server. Uses two ports, one for sending messages (write)
|
||||
/// and one for receiving messages (read).
|
||||
///
|
||||
/// Writing and reading are executed in two different threads. Each thread has
|
||||
/// its own queue of messages.
|
||||
///
|
||||
/// Note that a new socket is created for every connection (every write and
|
||||
/// read).
|
||||
class CARLA_API CarlaServer : private NonCopyable {
|
||||
public:
|
||||
|
||||
/// Starts two threads for writing and reading.
|
||||
explicit CarlaServer(int writePort, int readPort);
|
||||
|
||||
~CarlaServer();
|
||||
|
||||
/// Push a string to the sending queue.
|
||||
void writeString(const std::string &message);
|
||||
|
||||
/// Try to read a string from the receiving queue. Return false if the queue
|
||||
/// is empty.
|
||||
bool tryReadString(std::string &message);
|
||||
|
||||
private:
|
||||
|
||||
class Pimpl;
|
||||
const std::unique_ptr<Pimpl> _pimpl;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace carla
|
|
@ -0,0 +1,47 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#include "Carla.h"
|
||||
#include "TCPServer.h"
|
||||
|
||||
namespace carla {
|
||||
namespace server {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
TCPServer::TCPServer(int port) :
|
||||
_service(),
|
||||
_acceptor(_service, tcp::endpoint(tcp::v4(), port)) {}
|
||||
|
||||
TCPServer::~TCPServer() {}
|
||||
|
||||
void TCPServer::writeString(const std::string &message, error_code &error) {
|
||||
tcp::socket socket(_service);
|
||||
_acceptor.accept(socket);
|
||||
|
||||
boost::asio::write(socket, boost::asio::buffer(message), error);
|
||||
}
|
||||
|
||||
void TCPServer::readString(std::string &message, error_code &error) {
|
||||
tcp::socket socket(_service);
|
||||
_acceptor.accept(socket);
|
||||
|
||||
for (;; ) {
|
||||
std::array<char, 128> buf;
|
||||
|
||||
size_t len = socket.read_some(boost::asio::buffer(buf), error);
|
||||
|
||||
if (error == boost::asio::error::eof) {
|
||||
break; // Connection closed cleanly by peer.
|
||||
} else if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo find a better way.
|
||||
for (size_t i = 0u; i < len; ++i) {
|
||||
message += buf[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace carla
|
|
@ -0,0 +1,36 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace carla {
|
||||
namespace server {
|
||||
|
||||
/// Synchronous TCP server.
|
||||
///
|
||||
/// A new socket is created for every connection (every write and read).
|
||||
class CARLA_API TCPServer : private NonCopyable {
|
||||
public:
|
||||
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
explicit TCPServer(int port);
|
||||
|
||||
~TCPServer();
|
||||
|
||||
void writeString(const std::string &message, error_code &error);
|
||||
|
||||
void readString(std::string &message, error_code &error);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service _service;
|
||||
|
||||
boost::asio::ip::tcp::acceptor _acceptor;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace carla
|
|
@ -0,0 +1,58 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <carla/thread/ThreadSafeQueue.h>
|
||||
#include <carla/thread/ThreadUniquePointer.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace carla {
|
||||
namespace thread {
|
||||
|
||||
/// Executes the given job asynchronously for each item added to the queue.
|
||||
///
|
||||
/// The job gets called each time an item is added to the queue, the item is
|
||||
/// passed as argument.
|
||||
template <typename T>
|
||||
class CARLA_API AsyncReaderJobQueue {
|
||||
public:
|
||||
|
||||
using Job = std::function<void(T)>;
|
||||
|
||||
explicit AsyncReaderJobQueue(Job &&job) :
|
||||
_done(false),
|
||||
_job(std::move(job)),
|
||||
_queue(),
|
||||
_thread(new std::thread(&AsyncReaderJobQueue::workerThread, this)) {}
|
||||
|
||||
~AsyncReaderJobQueue() {
|
||||
_done = true;
|
||||
}
|
||||
|
||||
void push(T item) {
|
||||
_queue.push(item);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void workerThread() {
|
||||
while (!_done) {
|
||||
T value;
|
||||
_queue.wait_and_pop(value);
|
||||
_job(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic_bool _done;
|
||||
|
||||
Job _job;
|
||||
|
||||
ThreadSafeQueue<T> _queue;
|
||||
|
||||
const ThreadUniquePointer _thread;
|
||||
};
|
||||
|
||||
} // namespace thread
|
||||
} // namespace carla
|
|
@ -0,0 +1,54 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <carla/thread/ThreadSafeQueue.h>
|
||||
#include <carla/thread/ThreadUniquePointer.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace carla {
|
||||
namespace thread {
|
||||
|
||||
/// Executes the given job asynchronously. Every item that the job returns is
|
||||
/// added to the queue.
|
||||
template <typename T>
|
||||
class CARLA_API AsyncWriterJobQueue {
|
||||
public:
|
||||
|
||||
using Job = std::function<T()>;
|
||||
|
||||
explicit AsyncWriterJobQueue(Job &&job) :
|
||||
_done(false),
|
||||
_job(std::move(job)),
|
||||
_queue(),
|
||||
_thread(new std::thread(&AsyncWriterJobQueue::workerThread, this)) {}
|
||||
|
||||
~AsyncWriterJobQueue() {
|
||||
_done = true;
|
||||
}
|
||||
|
||||
bool tryPop(T &value) {
|
||||
return _queue.try_pop(value);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void workerThread() {
|
||||
while (!_done) {
|
||||
_queue.push(_job());
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic_bool _done;
|
||||
|
||||
Job _job;
|
||||
|
||||
ThreadSafeQueue<T> _queue;
|
||||
|
||||
const ThreadUniquePointer _thread;
|
||||
};
|
||||
|
||||
} // namespace thread
|
||||
} // namespace carla
|
|
@ -0,0 +1,83 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
// #include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace carla {
|
||||
namespace thread {
|
||||
|
||||
/// A thread safe queue.
|
||||
///
|
||||
/// From "C++ Concurrency In Action", Anthony Williams, listing 4.5.
|
||||
template<typename T>
|
||||
class CARLA_API ThreadSafeQueue {
|
||||
public:
|
||||
|
||||
ThreadSafeQueue() = default;
|
||||
|
||||
ThreadSafeQueue(const ThreadSafeQueue &other) {
|
||||
std::lock_guard<std::mutex> lock(other._mutex);
|
||||
_queue = other._queue;
|
||||
}
|
||||
|
||||
void push(T new_value) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_queue.push(new_value);
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
void wait_and_pop(T &value) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_condition.wait(lock, [this] {return !_queue.empty(); });
|
||||
value = _queue.front();
|
||||
_queue.pop();
|
||||
}
|
||||
|
||||
// std::shared_ptr<T> wait_and_pop() {
|
||||
// std::unique_lock<std::mutex> lock(_mutex);
|
||||
// _condition.wait(lock, [this] {return !_queue.empty(); });
|
||||
// std::shared_ptr<T> res(std::make_shared<T>(_queue.front()));
|
||||
// _queue.pop();
|
||||
// return res;
|
||||
// }
|
||||
|
||||
bool try_pop(T &value) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (_queue.empty()) {
|
||||
return false;
|
||||
}
|
||||
value = _queue.front();
|
||||
_queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
// std::shared_ptr<T> try_pop() {
|
||||
// std::lock_guard<std::mutex> lock(_mutex);
|
||||
// if (_queue.empty()) {
|
||||
// return std::shared_ptr<T>();
|
||||
// }
|
||||
// std::shared_ptr<T> res(std::make_shared<T>(_queue.front()));
|
||||
// _queue.pop();
|
||||
// return res;
|
||||
// }
|
||||
|
||||
bool empty() const {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
std::queue<T> _queue;
|
||||
|
||||
std::condition_variable _condition;
|
||||
};
|
||||
|
||||
} // namespace thread
|
||||
} // namespace carla
|
|
@ -0,0 +1,30 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace carla {
|
||||
namespace thread {
|
||||
|
||||
template<typename T>
|
||||
class JoinAndDeletePointer {
|
||||
public:
|
||||
|
||||
void operator()(T *ptr) {
|
||||
if (ptr) {
|
||||
if (ptr->joinable()) {
|
||||
ptr->join();
|
||||
}
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using ThreadUniquePointer =
|
||||
std::unique_ptr<std::thread, JoinAndDeletePointer<std::thread>>;
|
||||
|
||||
} // namespace thread
|
||||
} // namespace carla
|
|
@ -0,0 +1,8 @@
|
|||
add_executable(test_async_server async_server.cpp)
|
||||
target_link_libraries(test_async_server carla_server ${Boost_SYSTEM_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_REGEX_LIBRARY})
|
||||
|
||||
add_executable(test_sync_server sync_server.cpp)
|
||||
target_link_libraries(test_sync_server carla_server ${Boost_SYSTEM_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_REGEX_LIBRARY})
|
||||
|
||||
add_executable(test_client client.cpp)
|
||||
target_link_libraries(test_client carla_server ${Boost_SYSTEM_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_REGEX_LIBRARY})
|
|
@ -0,0 +1,66 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#include "Carla.h"
|
||||
|
||||
#include <carla/server/CarlaServer.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
enum ErrorCodes {
|
||||
InvalidArguments,
|
||||
STLException,
|
||||
UnknownException,
|
||||
ErrorSending,
|
||||
ErrorReading
|
||||
};
|
||||
|
||||
static int toInt(const std::string &str) {
|
||||
return std::stoi(str);
|
||||
}
|
||||
|
||||
static std::string daytimeString() {
|
||||
using namespace std;
|
||||
time_t now = time(0);
|
||||
std::string str = ctime(&now);
|
||||
return str;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
try {
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: server <send-port> <read-port>" << std::endl;
|
||||
return InvalidArguments;
|
||||
}
|
||||
|
||||
// This already starts the two threads.
|
||||
carla::server::CarlaServer server(toInt(argv[1u]), toInt(argv[2u]));
|
||||
|
||||
// Let's simulate the game loop.
|
||||
for (;;) {
|
||||
std::cout << "Sending..." << std::endl;
|
||||
auto time = daytimeString();
|
||||
server.writeString(time);
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
||||
std::cout << "Listening..." << std::endl;
|
||||
std::string message;
|
||||
if (server.tryReadString(message)) {
|
||||
std::cout << "Received: " << message << std::endl;
|
||||
if ((message == "q") || (message == "quit"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return STLException;
|
||||
} catch (...) {
|
||||
std::cerr << "Unknown exception thrown" << std::endl;
|
||||
return UnknownException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#include "Carla.h"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
enum ErrorCodes {
|
||||
InvalidArguments,
|
||||
STLException,
|
||||
UnknownException
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
try {
|
||||
if (argc != 4) {
|
||||
std::cerr << "Usage: client <host-ip> <read-port> <send-port>" << std::endl;
|
||||
return InvalidArguments;
|
||||
}
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
tcp::resolver resolver(io_service);
|
||||
tcp::resolver::query read_query(argv[1], argv[2]);
|
||||
tcp::resolver::iterator read_endpoint_iterator = resolver.resolve(read_query);
|
||||
tcp::resolver::query send_query(argv[1], argv[3]);
|
||||
tcp::resolver::iterator send_endpoint_iterator = resolver.resolve(send_query);
|
||||
|
||||
for (;;) {
|
||||
// Read message.
|
||||
{
|
||||
tcp::socket socket(io_service);
|
||||
boost::asio::connect(socket, read_endpoint_iterator);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::array<char, 128> buf;
|
||||
boost::system::error_code error;
|
||||
|
||||
size_t len = socket.read_some(boost::asio::buffer(buf), error);
|
||||
|
||||
if (error == boost::asio::error::eof)
|
||||
break; // Connection closed cleanly by peer.
|
||||
else if (error)
|
||||
throw boost::system::system_error(error); // Some other error.
|
||||
|
||||
std::cout.write(buf.data(), len);
|
||||
}
|
||||
}
|
||||
|
||||
// Send message.
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::string reply;
|
||||
std::cin >> reply;
|
||||
|
||||
tcp::socket socket(io_service);
|
||||
boost::asio::connect(socket, send_endpoint_iterator);
|
||||
|
||||
boost::system::error_code error;
|
||||
boost::asio::write(socket, boost::asio::buffer(reply), error);
|
||||
if (error)
|
||||
throw boost::system::system_error(error);
|
||||
|
||||
if ((reply == "q") || (reply == "quit"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return STLException;
|
||||
} catch (...) {
|
||||
std::cerr << "Unknown exception thrown" << std::endl;
|
||||
return UnknownException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
|
||||
|
||||
#include "Carla.h"
|
||||
|
||||
#include <carla/server/TCPServer.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
enum ErrorCodes {
|
||||
InvalidArguments,
|
||||
STLException,
|
||||
UnknownException,
|
||||
ErrorSending,
|
||||
ErrorReading
|
||||
};
|
||||
|
||||
static int toInt(const std::string &str) {
|
||||
return std::stoi(str);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
try {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage: server <port>" << std::endl;
|
||||
return InvalidArguments;
|
||||
}
|
||||
|
||||
using namespace carla::server;
|
||||
|
||||
TCPServer server(toInt(argv[1u]));
|
||||
|
||||
// Send message.
|
||||
{
|
||||
const std::string message = "What's up?";
|
||||
std::cout << "Sending " << message << "..." << std::endl;
|
||||
TCPServer::error_code error;
|
||||
server.writeString(message, error);
|
||||
if (error) {
|
||||
std::cerr << "Error sending: " << error.message() << std::endl;
|
||||
return ErrorSending;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Read message.
|
||||
{
|
||||
std::cout << "Reading..." << std::endl;
|
||||
std::string message;
|
||||
TCPServer::error_code error;
|
||||
server.readString(message, error);
|
||||
if (error && (error != boost::asio::error::eof)) {
|
||||
std::cerr << "Error reading: " << error.message() << std::endl;
|
||||
return ErrorReading;
|
||||
}
|
||||
|
||||
std::cout << "They said " << message << std::endl;
|
||||
}
|
||||
|
||||
// Send reply.
|
||||
{
|
||||
std::cout << "What do I say now?" << std::endl;
|
||||
std::string message;
|
||||
std::cin >> message;
|
||||
|
||||
if ((message == "q") || (message == "quit"))
|
||||
break;
|
||||
|
||||
TCPServer::error_code error;
|
||||
server.writeString(message, error);
|
||||
if (error) {
|
||||
std::cerr << "Error sending: " << error.message() << std::endl;
|
||||
return ErrorSending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return STLException;
|
||||
} catch (...) {
|
||||
std::cerr << "Unknown exception thrown" << std::endl;
|
||||
return UnknownException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
class CARLA_API NonCopyable {
|
||||
public:
|
||||
|
||||
NonCopyable() = default;
|
||||
|
||||
NonCopyable(const NonCopyable &) = delete;
|
||||
|
||||
void operator=(const NonCopyable &x) = delete;
|
||||
};
|
Loading…
Reference in New Issue