diff --git a/Client/Client.vcxproj b/Client/Client.vcxproj
new file mode 100644
index 0000000..8b59325
--- /dev/null
+++ b/Client/Client.vcxproj
@@ -0,0 +1,94 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {60BA9FBE-D2BA-4422-AF9A-652FF009EA46}
+ Win32Proj
+ Client
+
+
+
+ Application
+ true
+ v120
+ Unicode
+
+
+ Application
+ false
+ v120
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(VC_IncludePath);$(WindowsSDK_IncludePath);$(BOOST_ROOT);
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(BOOST_ROOT)\lib32-msvc-12.0;
+
+
+ false
+ $(VC_IncludePath);$(WindowsSDK_IncludePath);$(BOOST_ROOT);
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(BOOST_ROOT)\lib32-msvc-12.0;
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_WIN32_WINNT=0x0601;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;_WIN32_WINNT=0x0601;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Client/Client.vcxproj.filters b/Client/Client.vcxproj.filters
new file mode 100644
index 0000000..d69994e
--- /dev/null
+++ b/Client/Client.vcxproj.filters
@@ -0,0 +1,36 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/Client/client.cpp b/Client/client.cpp
new file mode 100644
index 0000000..e3fc23e
--- /dev/null
+++ b/Client/client.cpp
@@ -0,0 +1,75 @@
+#include
+#include
+
+#include
+#include
+
+#include "client.h"
+
+
+Client::Client(IoService& t_ioService, TcpResolverIterator t_endpointIterator,
+ std::string const& t_path)
+ : m_ioService(t_ioService), m_socket(t_ioService),
+ m_endpointIterator(t_endpointIterator), m_path(t_path)
+{
+ doConnect();
+ openFile(m_path);
+}
+
+
+void Client::openFile(std::string const& t_path)
+{
+ m_sourceFile.open(t_path, std::ios_base::binary | std::ios_base::ate);
+ if (m_sourceFile.fail())
+ throw std::fstream::failure("Failed while opening file " + t_path);
+
+ m_sourceFile.seekg(0, m_sourceFile.end);
+ auto fileSize = m_sourceFile.tellg();
+ m_sourceFile.seekg(0, m_sourceFile.beg);
+
+ std::ostream requestStream(&m_request);
+ boost::filesystem::path p(t_path);
+ requestStream << p.filename().string() << "\n" << fileSize << "\n\n";
+ BOOST_LOG_TRIVIAL(trace) << "Request size: " << m_request.size();
+}
+
+
+void Client::doConnect()
+{
+ boost::asio::async_connect(m_socket, m_endpointIterator,
+ [this](boost::system::error_code ec, TcpResolverIterator)
+ {
+ if (!ec) {
+ writeBuffer(m_request);
+ } else {
+ std::cout << "Coudn't connect to host. Please run server "
+ "or check network connection.\n";
+ BOOST_LOG_TRIVIAL(error) << "Error: " << ec.message();
+ }
+ });
+}
+
+
+void Client::doWriteFile(const boost::system::error_code& t_ec)
+{
+ if (!t_ec) {
+ if (m_sourceFile) {
+ m_sourceFile.read(m_buf.data(), m_buf.size());
+ if (m_sourceFile.fail() && !m_sourceFile.eof()) {
+ auto msg = "Failed while reading file";
+ BOOST_LOG_TRIVIAL(error) << msg;
+ throw std::fstream::failure(msg);
+ }
+ std::stringstream ss;
+ ss << "Send " << m_sourceFile.gcount() << " bytes, total: "
+ << m_sourceFile.tellg() << " bytes";
+ BOOST_LOG_TRIVIAL(trace) << ss.str();
+ std::cout << ss.str() << std::endl;
+
+ auto buf = boost::asio::buffer(m_buf.data(), static_cast(m_sourceFile.gcount()));
+ writeBuffer(buf);
+ }
+ } else {
+ BOOST_LOG_TRIVIAL(error) << "Error: " << t_ec.message();
+ }
+}
diff --git a/Client/client.h b/Client/client.h
new file mode 100644
index 0000000..7f2ffb1
--- /dev/null
+++ b/Client/client.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+
+class Client
+{
+public:
+ using IoService = boost::asio::io_service;
+ using TcpResolver = boost::asio::ip::tcp::resolver;
+ using TcpResolverIterator = TcpResolver::iterator;
+ using TcpSocket = boost::asio::ip::tcp::socket;
+
+ Client(IoService& t_ioService, TcpResolverIterator t_endpointIterator,
+ std::string const& t_path);
+
+private:
+ void openFile(std::string const& t_path);
+ void doConnect();
+ void doWriteFile(const boost::system::error_code& t_ec);
+ template
+ void writeBuffer(Buffer& t_buffer);
+
+
+ TcpResolver m_ioService;
+ TcpSocket m_socket;
+ TcpResolverIterator m_endpointIterator;
+ enum { MessageSize = 1024 };
+ std::array m_buf;
+ boost::asio::streambuf m_request;
+ std::ifstream m_sourceFile;
+ std::string m_path;
+};
+
+
+template
+void Client::writeBuffer(Buffer& t_buffer)
+{
+ boost::asio::async_write(m_socket,
+ t_buffer,
+ [this](boost::system::error_code ec, size_t /*length*/)
+ {
+ doWriteFile(ec);
+ });
+}
diff --git a/Client/main.cpp b/Client/main.cpp
new file mode 100644
index 0000000..a8cf35c
--- /dev/null
+++ b/Client/main.cpp
@@ -0,0 +1,37 @@
+#include
+
+#include
+
+#include "client.h"
+#include "../Common/logger.h"
+
+
+int main(int argc, char* argv[])
+{
+ if (argc != 4) {
+ std::cerr << "Usage: client \n";
+ return 1;
+ }
+
+ Logger::instance().setOptions("client_%3N.log", 1 * 1024 * 1024, 10 * 1024 * 1024);
+
+ auto address = argv[1];
+ auto port = argv[2];
+ auto filePath = argv[3];
+
+ try {
+ boost::asio::io_service ioService;
+
+ boost::asio::ip::tcp::resolver resolver(ioService);
+ auto endpointIterator = resolver.resolve({ address, port });
+ Client client(ioService, endpointIterator, filePath);
+
+ ioService.run();
+ } catch (std::fstream::failure& e) {
+ std::cerr << e.what() << "\n";
+ } catch (std::exception& e) {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/Common/logger.cpp b/Common/logger.cpp
new file mode 100644
index 0000000..d87938d
--- /dev/null
+++ b/Common/logger.cpp
@@ -0,0 +1,28 @@
+#include
+#include
+
+#include "logger.h"
+
+
+Logger& Logger::instance()
+{
+ static Logger logger;
+ return logger;
+}
+
+
+void Logger::setOptions(std::string const& t_fileName, unsigned t_rotationSize,
+ unsigned t_maxSize)
+{
+ boost::log::add_file_log(
+ boost::log::keywords::file_name = t_fileName,
+ boost::log::keywords::rotation_size = t_rotationSize,
+ boost::log::keywords::max_size = t_maxSize,
+ boost::log::keywords::time_based_rotation
+ = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
+ boost::log::keywords::format = "[%TimeStamp%]: %Message%",
+ boost::log::keywords::auto_flush = true
+ );
+
+ boost::log::add_common_attributes();
+}
diff --git a/Common/logger.h b/Common/logger.h
new file mode 100644
index 0000000..7e545af
--- /dev/null
+++ b/Common/logger.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+
+
+class Logger
+{
+ Logger() {}
+public:
+ static Logger& instance();
+ static void setOptions(std::string const& t_fileName, unsigned t_rotationSize,
+ unsigned t_maxSize);
+};
\ No newline at end of file
diff --git a/FileTransfer.sln b/FileTransfer.sln
new file mode 100644
index 0000000..99e7446
--- /dev/null
+++ b/FileTransfer.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.40418.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Client", "Client\Client.vcxproj", "{60BA9FBE-D2BA-4422-AF9A-652FF009EA46}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{49207E11-2770-4624-ACBC-CB9D1CD4A6A9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {60BA9FBE-D2BA-4422-AF9A-652FF009EA46}.Debug|Win32.ActiveCfg = Debug|Win32
+ {60BA9FBE-D2BA-4422-AF9A-652FF009EA46}.Debug|Win32.Build.0 = Debug|Win32
+ {60BA9FBE-D2BA-4422-AF9A-652FF009EA46}.Release|Win32.ActiveCfg = Release|Win32
+ {60BA9FBE-D2BA-4422-AF9A-652FF009EA46}.Release|Win32.Build.0 = Release|Win32
+ {49207E11-2770-4624-ACBC-CB9D1CD4A6A9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {49207E11-2770-4624-ACBC-CB9D1CD4A6A9}.Debug|Win32.Build.0 = Debug|Win32
+ {49207E11-2770-4624-ACBC-CB9D1CD4A6A9}.Release|Win32.ActiveCfg = Release|Win32
+ {49207E11-2770-4624-ACBC-CB9D1CD4A6A9}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5a129b2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Requirements
+- MS Visual Studio 2015 with update 5
+- boost 1.58.0
+
+# Building for Windows
+- Install boost.asio
+- Add `BOOST_ROOT` with boost's directory path to environment variables
+- Open solution and build
+
+# Running
+- start server to accept files
+```
+server
+```
+- start client to transfer file
+```
+client
+```
+
diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj
new file mode 100644
index 0000000..ea64aa8
--- /dev/null
+++ b/Server/Server.vcxproj
@@ -0,0 +1,94 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {49207E11-2770-4624-ACBC-CB9D1CD4A6A9}
+ Win32Proj
+ Server
+
+
+
+ Application
+ true
+ v120
+ Unicode
+
+
+ Application
+ false
+ v120
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(VC_IncludePath);$(WindowsSDK_IncludePath);$(BOOST_ROOT);
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(BOOST_ROOT)\lib32-msvc-12.0;
+
+
+ false
+ $(VC_IncludePath);$(WindowsSDK_IncludePath);$(BOOST_ROOT);
+ $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(BOOST_ROOT)\lib32-msvc-12.0;
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_WIN32_WINNT=0x0601;WIN32_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;_WIN32_WINNT=0x0601;WIN32NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Server/Server.vcxproj.filters b/Server/Server.vcxproj.filters
new file mode 100644
index 0000000..9e2d290
--- /dev/null
+++ b/Server/Server.vcxproj.filters
@@ -0,0 +1,36 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/Server/main.cpp b/Server/main.cpp
new file mode 100644
index 0000000..7687bb1
--- /dev/null
+++ b/Server/main.cpp
@@ -0,0 +1,29 @@
+#include
+
+#include
+
+#include "server.h"
+#include "../Common/logger.h"
+
+
+int main(int argc, char* argv[])
+{
+ if (argc != 3) {
+ std::cerr << "Usage: server \n";
+ return 1;
+ }
+
+ Logger::instance().setOptions("server_%3N.log", 1 * 1024 * 1024, 10 * 1024 * 1024);
+
+ try {
+ boost::asio::io_service ioService;
+
+ Server server(ioService, std::stoi(argv[1]), argv[2]);
+
+ ioService.run();
+ } catch (std::exception& e) {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
diff --git a/Server/server.cpp b/Server/server.cpp
new file mode 100644
index 0000000..37fb58d
--- /dev/null
+++ b/Server/server.cpp
@@ -0,0 +1,146 @@
+#include
+
+#include
+#include
+#include
+
+#include "server.h"
+
+
+Session::Session(TcpSocket t_socket)
+ : m_socket(std::move(t_socket))
+{
+}
+
+
+void Session::doRead()
+{
+ auto self = shared_from_this();
+ async_read_until(m_socket, m_requestBuf_, "\n\n",
+ [this, self](boost::system::error_code ec, size_t bytes)
+ {
+ if (!ec)
+ processRead(bytes);
+ else
+ handleError(__FUNCTION__, ec);
+ });
+}
+
+
+void Session::processRead(size_t t_bytesTransferred)
+{
+ BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << "(" << t_bytesTransferred << ")"
+ << ", in_avail = " << m_requestBuf_.in_avail() << ", size = "
+ << m_requestBuf_.size() << ", max_size = " << m_requestBuf_.max_size() << ".";
+
+ std::istream requestStream(&m_requestBuf_);
+ readData(requestStream);
+
+ auto pos = m_fileName.find_last_of('\\');
+ if (pos != std::string::npos)
+ m_fileName = m_fileName.substr(pos + 1);
+
+ createFile();
+
+ // write extra bytes to file
+ do {
+ requestStream.read(m_buf.data(), m_buf.size());
+ BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " write " << requestStream.gcount() << " bytes.";
+ m_outputFile.write(m_buf.data(), requestStream.gcount());
+ } while (requestStream.gcount() > 0);
+
+ auto self = shared_from_this();
+ m_socket.async_read_some(boost::asio::buffer(m_buf.data(), m_buf.size()),
+ [this, self](boost::system::error_code ec, size_t bytes)
+ {
+ if (!ec)
+ doReadFileContent(bytes);
+ else
+ handleError(__FUNCTION__, ec);
+ });
+}
+
+
+void Session::readData(std::istream &stream)
+{
+ stream >> m_fileName;
+ stream >> m_fileSize;
+ stream.read(m_buf.data(), 2);
+
+ BOOST_LOG_TRIVIAL(trace) << m_fileName << " size is " << m_fileSize
+ << ", tellg = " << stream.tellg();
+}
+
+
+void Session::createFile()
+{
+ m_outputFile.open(m_fileName, std::ios_base::binary);
+ if (!m_outputFile) {
+ BOOST_LOG_TRIVIAL(error) << __LINE__ << ": Failed to create: " << m_fileName;
+ return;
+ }
+}
+
+
+void Session::doReadFileContent(size_t t_bytesTransferred)
+{
+ if (t_bytesTransferred > 0) {
+ m_outputFile.write(m_buf.data(), static_cast(t_bytesTransferred));
+
+ BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " recv " << m_outputFile.tellp() << " bytes";
+
+ if (m_outputFile.tellp() >= static_cast(m_fileSize)) {
+ std::cout << "Received file: " << m_fileName << std::endl;
+ return;
+ }
+ }
+ auto self = shared_from_this();
+ m_socket.async_read_some(boost::asio::buffer(m_buf.data(), m_buf.size()),
+ [this, self](boost::system::error_code ec, size_t bytes)
+ {
+ doReadFileContent(bytes);
+ });
+}
+
+
+void Session::handleError(std::string const& t_functionName, boost::system::error_code const& t_ec)
+{
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " in " << t_functionName << " due to "
+ << t_ec << " " << t_ec.message() << std::endl;
+}
+
+
+Server::Server(IoService& t_ioService, short t_port, std::string const& t_workDirectory)
+ : m_socket(t_ioService),
+ m_acceptor(t_ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), t_port)),
+ m_workDirectory(t_workDirectory)
+{
+ std::cout << "Server started\n";
+
+ createWorkDirectory();
+
+ doAccept();
+}
+
+
+void Server::doAccept()
+{
+ m_acceptor.async_accept(m_socket,
+ [this](boost::system::error_code ec)
+ {
+ if (!ec)
+ std::make_shared(std::move(m_socket))->start();
+
+ doAccept();
+ });
+}
+
+
+void Server::createWorkDirectory()
+{
+ using namespace boost::filesystem;
+ auto currentPath = path(m_workDirectory);
+ if (!exists(currentPath) && !create_directory(currentPath))
+ BOOST_LOG_TRIVIAL(error) << "Coudn't create working directory: " << m_workDirectory;
+ current_path(currentPath);
+}
diff --git a/Server/server.h b/Server/server.h
new file mode 100644
index 0000000..eaaf8d0
--- /dev/null
+++ b/Server/server.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+
+class Session
+ : public std::enable_shared_from_this
+{
+public:
+ using TcpSocket = boost::asio::ip::tcp::socket;
+
+ Session(TcpSocket t_socket);
+
+ void start()
+ {
+ doRead();
+ }
+
+private:
+ void doRead();
+ void processRead(size_t t_bytesTransferred);
+ void createFile();
+ void readData(std::istream &stream);
+ void doReadFileContent(size_t t_bytesTransferred);
+ void handleError(std::string const& t_functionName, boost::system::error_code const& t_ec);
+
+
+ TcpSocket m_socket;
+ enum { MaxLength = 40960 };
+ std::array m_buf;
+ boost::asio::streambuf m_requestBuf_;
+ std::ofstream m_outputFile;
+ size_t m_fileSize;
+ std::string m_fileName;
+};
+
+
+class Server
+{
+public:
+ using TcpSocket = boost::asio::ip::tcp::socket;
+ using TcpAcceptor = boost::asio::ip::tcp::acceptor;
+ using IoService = boost::asio::io_service;
+
+ Server(IoService& t_ioService, short t_port, std::string const& t_workDirectory);
+
+private:
+ void doAccept();
+ void createWorkDirectory();
+
+ TcpSocket m_socket;
+ TcpAcceptor m_acceptor;
+
+ std::string m_workDirectory;
+};