├── .circleci └── config.yml ├── .gitignore ├── .gitmodules ├── FileBroadcaster.sln ├── LICENSE ├── Makefile ├── README.md ├── src ├── Config.hpp ├── FileBroadcaster.vcxproj ├── FileBroadcaster.vcxproj.filters ├── FileBroadcaster.vcxproj.user ├── Main.cpp ├── Receiver.cpp ├── Sender.cpp └── Utils.hpp └── tests ├── Tests.cpp ├── packages.config └── tests.vcxproj /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | gtest: 4 | working_directory: ~/root 5 | docker: 6 | - image: gcc:latest 7 | steps: 8 | - run: 9 | name: Downloading dependencies 10 | command: | 11 | apt-get update 12 | apt-get install -y cmake 13 | apt-get install -y libgtest-dev 14 | - run: 15 | name: Installing dependencies 16 | working_directory: /usr/src/gtest/ 17 | command: | 18 | cmake CMakeLists.txt 19 | make 20 | cp /usr/src/gtest/*.a /usr/lib 21 | - checkout 22 | - run: 23 | name: "Pull Submodules" 24 | command: | 25 | git submodule init 26 | git submodule update --recursive --remote 27 | - run: 28 | name: Building tests 29 | command: make gtests 30 | - run: 31 | name: Running tests 32 | command: ./GTests --gtest_filter=* 33 | workflows: 34 | version: 2 35 | build_and_test: 36 | jobs: 37 | - gtest: 38 | filters: 39 | branches: 40 | only: 41 | - master 42 | - develop 43 | - tests -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | #Visual studio dirs 35 | .vs/ 36 | x64/ 37 | x86/ 38 | Debug/ 39 | Release/ 40 | packages/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/cxxopts"] 2 | path = lib/cxxopts 3 | url = https://github.com/jarro2783/cxxopts 4 | -------------------------------------------------------------------------------- /FileBroadcaster.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2047 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileBroadcaster", "src\FileBroadcaster.vcxproj", "{CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests\tests.vcxproj", "{6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Debug|x64.ActiveCfg = Debug|x64 19 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Debug|x64.Build.0 = Debug|x64 20 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Debug|x86.ActiveCfg = Debug|Win32 21 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Debug|x86.Build.0 = Debug|Win32 22 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Release|x64.ActiveCfg = Release|x64 23 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Release|x64.Build.0 = Release|x64 24 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Release|x86.ActiveCfg = Release|Win32 25 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E}.Release|x86.Build.0 = Release|Win32 26 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Debug|x64.ActiveCfg = Debug|x64 27 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Debug|x64.Build.0 = Debug|x64 28 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Debug|x86.ActiveCfg = Debug|Win32 29 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Debug|x86.Build.0 = Debug|Win32 30 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Release|x64.ActiveCfg = Release|x64 31 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Release|x64.Build.0 = Release|x64 32 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Release|x86.ActiveCfg = Release|Win32 33 | {6E714D4E-EAB6-43B3-AD29-FB75743CA1BD}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {08B5E1AD-5683-4253-9091-883D76F7F554} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | program: 2 | g++ src/Main.cpp \ 3 | -std=c++14 -pthread \ 4 | -Ilib/cxxopts/include \ 5 | -o FileBroadcaster 6 | 7 | gtests: 8 | g++ tests/Tests.cpp \ 9 | -std=c++14 -pthread \ 10 | -Ilib/cxxopts/include \ 11 | -lgtest \ 12 | -o GTests 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # File-Broadcaster 2 | 3 |

4 | 5 | Build status 6 | 7 | Code quality 8 | 9 | Release 10 | 11 | Code quality 12 | 13 | License 14 |

15 | 16 | UDP File sender and receiver 17 | Can use broadcast address to send file on all computers in LAN 18 | 19 | ## Features 20 | 21 | - Send file to one or all computers in LAN 22 | - Reliability of data transmission 23 | - Server timeout detection 24 | - Change MTU 25 | 26 | ## Overview 27 | 28 | - [Requirements](#requirements) 29 | - [Download](#download) 30 | - [Installation](#installation) 31 | - [Script Parameters](#script-parameters) 32 | - [Packets Specification](#packets-specification) 33 | - [Script Specification](#script-specification) 34 | 35 | ## Download 36 | Clone the [source repository](http://github.com/gistrec/File-Broadcaster) from Github. 37 | * On the command line, enter: 38 | ```` 39 | git clone https://github.com/gistrec/File-Broadcaster.git 40 | git submodule init 41 | git submodule update --recursive --remote 42 | ```` 43 | 44 | * You can probably use [Github for Windows](http://windows.github.com/) or [Github for Mac](http://mac.github.com/) instead of the command line, however these aren't tested/supported and we only use the command line for development. Use [this link](https://git-scm.com/downloads) to download the command line version. 45 | 46 | 47 | ## Requirements 48 | * Windows: 49 | * Visual Studio 2015 or 2017 50 | * Linux: 51 | * g++ 52 | * pthread 53 | * arpa 54 | 55 | 56 | 57 | ## Installation 58 | * Windows 59 | * Open FileBroadcaster.sln via Visual Studio 60 | * Build project 61 | * Linux 62 | * Open a terminal/console/command prompt, change to the directory where you cloned project, and type: 63 | ```` 64 | make all 65 | ```` 66 | 67 | ## Script Parameters 68 | | Parameter | Default | Description | 69 | | ------ | -------- | -------- | 70 | | p, port | 33333 | Sender and receiver port | 71 | | f, filename| `none` | Transmitted and received file | 72 | | t, type | receiver | receiver or sender | 73 | | ttl | 15 | Seconds to wait cliend requests or sender responses | 74 | | mtu | 1500 | MTU packet size | 75 | | broadcast | 255.255.255.255 | Broadcast address. Can use to unicast. | 76 | 77 | ## Packets Specification 78 | Packets structure 79 | ![alt text](https://www.gistrec.ru/wp-content/uploads/2019/01/Packets.png) 80 | 81 | ## Script Specification 82 | 1. Sender send `NEW_PACKET` packet to broadcast (or unicast) address 83 | 2. Sender send all parts of file via `TRANSFER` packet 84 | 3. If any pacckets were lost, receiver ask them sending `RESEND` packet to broadcast (or unicast) address 85 | 4. Sender wait `RESEND` packets or wait TTL and turns off 86 | 5. Receiver ask all lost parts, until the whole file is no downloaded or wait TTL and turns off 87 | -------------------------------------------------------------------------------- /src/Config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEBROADCASTER_CONFIG_H 2 | #define FILEBROADCASTER_CONFIG_H 3 | 4 | #include "Utils.hpp" 5 | 6 | 7 | std::string fileName; // File Name to transfer or receive 8 | 9 | int mtu; // Max packet size to send and receive 10 | 11 | int ttl; // Current wait time for new packages before shutting down 12 | int ttl_max; // Maximum wait time for new packages before shutting down 13 | 14 | SOCKET _socket; 15 | 16 | SOCKADDR_IN server_address = { 0 }; 17 | SOCKADDR_IN client_address = { 0 }; 18 | SOCKADDR_IN broadcast_address = { 0 }; 19 | 20 | addr_len server_address_length = sizeof(server_address); 21 | addr_len client_address_length = sizeof(client_address); 22 | 23 | size_t file_length; // File size in bytes 24 | char* file = nullptr; // Pointer to file in RAM 25 | char* buffer = nullptr; // Pointer to buffer 26 | 27 | #endif //FILEBROADCASTER_CONFIG_H 28 | -------------------------------------------------------------------------------- /src/FileBroadcaster.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {CB24E1BF-BA1E-405A-809C-7EA285F3AF1E} 24 | FileBroadcaster 25 | 10.0.17763.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | 80 | 81 | 82 | 83 | Level3 84 | Disabled 85 | true 86 | true 87 | _MBCS;%(PreprocessorDefinitions); 88 | $(SolutionDir)/lib/cxxopts/include/ 89 | 90 | 91 | 92 | 93 | Level3 94 | MaxSpeed 95 | true 96 | true 97 | true 98 | true 99 | 100 | 101 | true 102 | true 103 | 104 | 105 | 106 | 107 | Level3 108 | MaxSpeed 109 | true 110 | true 111 | true 112 | true 113 | _MBCS;%(PreprocessorDefinitions); 114 | $(SolutionDir)/lib/cxxopts/include/ 115 | 116 | 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/FileBroadcaster.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/FileBroadcaster.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "cxxopts.hpp" 2 | 3 | #include "Receiver.cpp" 4 | #include "Sender.cpp" 5 | #include "Config.hpp" 6 | 7 | 8 | int main(int argc, char* argv[]) { 9 | // Parsing input parameters from the CLI 10 | cxxopts::Options options("File-Broadcaster", "UDB Broadcast file transfer"); 11 | 12 | options 13 | .positional_help("[optional args]") 14 | .show_positional_help(); 15 | 16 | options.add_options() 17 | ("f,file", "File name", cxxopts::value()->default_value("file.out")) 18 | ("t,type", "Receiver or sender", cxxopts::value()->default_value("sender")) 19 | ("broadcast", "Broadcast address", cxxopts::value()->default_value("yes")) 20 | ("p,port", "Port", cxxopts::value()->default_value("33333")) 21 | ("mtu", "MTU packet", cxxopts::value()->default_value("1500")) 22 | ("ttl", "Time to live", cxxopts::value()->default_value("15")); 23 | 24 | auto result = options.parse(argc, argv); 25 | 26 | #if defined(_WIN32) || defined(_WIN64) // 27 | WORD socketVer; // Initializing the use 28 | WSADATA wsaData; // of the Winsock DLL 29 | socketVer = MAKEWORD(2, 2); // by this process. 30 | WSAStartup(socketVer, &wsaData); // 31 | #endif // 32 | 33 | mtu = result["mtu"].as(); // 34 | ttl = result["ttl"].as(); // Initializing some variable 35 | ttl_max = result["ttl"].as(); // 36 | fileName = result["file"].as(); // 37 | 38 | 39 | _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); // 40 | if (_socket < 0) { // Create socket with 41 | std::cout << "Error: Can't create socket" << std::endl; // datagram-based protocol 42 | exit(1); // 43 | } else { // 44 | std::cout << "Ok: Socket created" << std::endl; // 45 | } // 46 | 47 | client_address.sin_family = AF_INET; // 48 | client_address.sin_port = htons(result["port"].as()); // Creating local address 49 | client_address.sin_addr.s_addr = INADDR_ANY; // 50 | 51 | memcpy(&server_address, &client_address, sizeof(server_address)); // Server address == Client address 52 | 53 | broadcast_address.sin_family = AF_INET; // Creating broadcast 54 | broadcast_address.sin_port = htons(result["port"].as()); // address 55 | 56 | if (result["broadcast"].as() == "yes") { // 57 | #if defined(_WIN32) || defined(_WIN64) // Getting access to 58 | char broadcastEnable = '1'; // the broadcast address 59 | #else // 60 | int broadcastEnable = 1; // 61 | #endif // 62 | // 63 | if (setsockopt(_socket, SOL_SOCKET, SO_BROADCAST, // 64 | &broadcastEnable, sizeof(broadcastEnable)) != 0) { // 65 | std::cout << "Ok: Got access to broadcast" << std::endl; // 66 | } else { // 67 | std::cerr << "Error: Cant't get access to broadcast" << std::endl; // 68 | exit(1); // 69 | } // If parameter "broadcast" is "yes", then 70 | broadcast_address.sin_addr.s_addr = INADDR_BROADCAST; // change server address 71 | } else { // to broadcast 72 | broadcast_address.sin_addr.s_addr = // Else change server address 73 | inet_addr(result["broadcast"].as().c_str()); // to address in parameter 74 | } // 75 | 76 | if (bind(_socket, (sockaddr *)&client_address, sizeof(client_address)) == 0) {// 77 | std::cout << "Ok: Socket binded" << std::endl; // 78 | } else { // Bind socket to 79 | std::cerr << "Error: Can't bind socket" << std::endl; // client address 80 | exit(1); // 81 | } // 82 | 83 | #if defined(_WIN32) || defined(_WIN64) // 84 | int tv = 1 * 1000; // user timeout in milliseconds [ms] // 85 | setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)); // Set socket 86 | #else // receive timeout 87 | struct timeval tv; // to 1 sec 88 | tv.tv_sec = 1; // 89 | tv.tv_usec = 0; // 90 | setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); // 91 | #endif 92 | 93 | // Run receiver or sender 94 | if (result["type"].as() == "receiver") { // 95 | Receiver::run(result); // 96 | } else if (result["type"].as() == "sender") { // Run receiver or sender 97 | Sender::run(result); // application 98 | } else { // 99 | std::cerr << "Error: Type not found" << std::endl; // 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /src/Receiver.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.hpp" 2 | #include "Config.hpp" 3 | 4 | 5 | namespace Receiver { 6 | 7 | /** 8 | * List of received parts 9 | */ 10 | std::set parts; 11 | 12 | /** 13 | * Get empty parts 14 | */ 15 | std::vector getEmptyParts() { 16 | std::vector result; 17 | // For each parts 18 | for (int i = 0; i < int((float)file_length / (float)mtu + 0.5); i++) { 19 | if (parts.find(i) == parts.end()) result.push_back(i); 20 | } 21 | return result; 22 | } 23 | 24 | /** 25 | * Runs when "FINISH" packet is received 26 | * Gets empty parts and requests them from the server 27 | */ 28 | void checkParts() { 29 | char buffer[100]; 30 | 31 | std::vector emptyParts = getEmptyParts(); 32 | 33 | while (ttl && emptyParts.size() > 0) { 34 | for (auto index : emptyParts) { 35 | snprintf(buffer, 7, "RESEND"); // 36 | Utils::writeBytesFromNumber(buffer + 6, index, 4); // Create request packet 37 | sendto(_socket, buffer, 10, 0, (sockaddr*) &broadcast_address,// 38 | sizeof(broadcast_address)); // 39 | 40 | std::cout << "Request part of file with index " << index << std::endl; 41 | } 42 | emptyParts = getEmptyParts(); 43 | } 44 | 45 | std::ofstream output(fileName, std::ofstream::binary); // 46 | output.write(file, file_length); // Save file 47 | 48 | std::cout << "File successfully received" << std::endl; 49 | 50 | delete[] file; 51 | exit(0); 52 | } 53 | 54 | 55 | void run(cxxopts::ParseResult &options) { 56 | bool finish = false; // Sender finish transfering 57 | 58 | char* buffer = new char[2 * mtu]; 59 | 60 | while (auto length = recvfrom(_socket, buffer, 2 * mtu, 0, (sockaddr*) &server_address, &server_address_length)) { 61 | // Sender is no longer available 62 | if (ttl <= 0) return; 63 | 64 | // If Sender finish transfering check missing parts every 1 sec 65 | if (finish) checkParts(); 66 | 67 | if (length <= 0) continue; // If no more packets in buffer 68 | 69 | ttl = ttl_max; // Update ttl 70 | 71 | if (strncmp(buffer, "NEW_PACKET", 10) == 0) { 72 | file_length = Utils::getNumberFromBytes(buffer + 10, 4); // Read section "file length" 73 | 74 | file = new char[file_length]; 75 | memset(file, 0, file_length); 76 | 77 | std::cout << "Receive information about new file size: " << file_length << std::endl; 78 | std::cout << "Number of parts: " << int((float)file_length / (float)mtu + 0.5) << std::endl; 79 | } else if (strncmp(buffer, "TRANSFER", 8) == 0) { 80 | int part = Utils::getNumberFromBytes(buffer + 8, 4); // Read section "index" 81 | int size = Utils::getNumberFromBytes(buffer + 12, 4); // Read section "size" 82 | parts.insert(part); 83 | std::cout << "Receive " << part << " part with size " << size << std::endl; 84 | 85 | memcpy(file + part * options["mtu"].as(), buffer + 16, size); 86 | } else if (strncmp(buffer, "FINISH", 6) == 0) { 87 | // If receiver didn't receive a finish message 88 | if (!finish) { 89 | std::cout << "Server finished transferring" << std::endl; 90 | finish = true; 91 | } 92 | } 93 | } 94 | delete[] buffer; 95 | } 96 | 97 | } //namespace Receiver -------------------------------------------------------------------------------- /src/Sender.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.hpp" 2 | #include "Config.hpp" 3 | 4 | 5 | namespace Sender { 6 | 7 | /** 8 | * Since server may receive many requests for sending some part 9 | * It is necessary to limit the sending of the same parts for a while 10 | * This container contains part number and time when the part was sended 11 | */ 12 | std::map sent_part; 13 | 14 | void sendPart(int part_index) { 15 | int packet_length = file_length - part_index * mtu; // Get packet length 16 | if (packet_length > mtu) packet_length = mtu; // 17 | 18 | snprintf(buffer, 9, "TRANSFER"); 19 | Utils::writeBytesFromNumber(buffer + 8, (size_t)part_index, 4); // Write section "number" 20 | Utils::writeBytesFromNumber(buffer + 12, (size_t)packet_length, 4); // Write section "length" 21 | memcpy(buffer + 16, (void *)(intptr_t)(file + part_index * mtu), packet_length); // Write section "data" 22 | 23 | // Sending part to the broadcast address 24 | sendto(_socket, buffer, packet_length + 16, 0, (struct sockaddr*) &broadcast_address, sizeof(broadcast_address)); 25 | std::cout << "Part " << part_index << " with size " << packet_length << " was send" << std::endl; 26 | } 27 | 28 | void run(cxxopts::ParseResult &options) { 29 | buffer = new char[2 * mtu]; 30 | 31 | std::ifstream input(fileName, std::ios::binary); // 32 | if (!input.is_open()) { // Opening the file 33 | std::cout << "Error: Can't open file " << fileName << std::endl; // 34 | exit(-1); // 35 | } // 36 | 37 | // Thk Windows.h, where define max(). It so horrible... // 38 | input.ignore((std::numeric_limits::max)()); // 39 | file_length = (int) input.gcount(); // Getting file length 40 | input.seekg(0, input.beg); // And writing file to RAM 41 | // 42 | file = new (std::nothrow) char[file_length + 1]; // 43 | if (!file) { // 44 | std::cout << "Error: Can't allocate " << file_length << " bytes" << std::endl; 45 | exit(-1); 46 | } 47 | input.read(file, file_length); 48 | 49 | std::cout << "Ok: File successfully copied to RAM" << std::endl; 50 | 51 | snprintf(buffer, 11, "NEW_PACKET"); // 52 | Utils::writeBytesFromNumber(buffer + 10, file_length, 4); // Sending information 53 | sendto(_socket, buffer, 14, 0, (sockaddr*) &broadcast_address, // about size of new file 54 | sizeof(broadcast_address)); // 55 | 56 | std::cout << "Ok: Send information about new file with size " << file_length << std::endl; 57 | 58 | int part_index = 0; 59 | 60 | while (part_index * mtu < file_length) { // 61 | sent_part.insert({ part_index, 0 }); // 62 | // 63 | sendPart(part_index); // Send parts 64 | // every 20ms 65 | part_index++; // 66 | std::this_thread::sleep_for(20ms); // 67 | } // 68 | 69 | snprintf(buffer, 7, "FINISH"); // Sending file transfer 70 | sendto(_socket, buffer, 6, 0, (sockaddr*) &broadcast_address, // completion information 71 | sizeof(broadcast_address)); // 72 | std::cout << "Ok: File transfer complete" << std::endl; // 73 | 74 | long lastFinishSendTime = 0; // Last time, when sender sended file transfer completion information 75 | 76 | while (ttl) { 77 | auto result = recvfrom(_socket, (char *)buffer, 100, 0, (struct sockaddr*) &broadcast_address, &client_address_length); 78 | 79 | // sending file completion information every second 80 | if (result <= 0) { 81 | ttl--; 82 | snprintf(buffer, 7, "FINISH"); 83 | sendto(_socket, buffer, 6, 0, (struct sockaddr*) &broadcast_address, sizeof(broadcast_address)); 84 | continue; 85 | } 86 | 87 | if (strncmp(buffer, "RESEND", 6) == 0) { 88 | int part = Utils::getNumberFromBytes(buffer + 6, 4); 89 | 90 | auto now = std::chrono::system_clock::now(); 91 | auto now_ms = std::chrono::time_point_cast(now); 92 | auto epoch = now_ms.time_since_epoch(); 93 | auto value = std::chrono::duration_cast(epoch); 94 | long duration = value.count(); // Unix time in second 95 | 96 | ttl = ttl_max; 97 | 98 | if (duration - sent_part[part] >= 1) { 99 | sent_part[part] = duration; 100 | std::cout << "Client requested part of file with index " << part << std::endl; 101 | sendPart(part); 102 | } 103 | 104 | // sending file completion information every second 105 | if (duration - lastFinishSendTime >= 1) { 106 | lastFinishSendTime = duration; 107 | snprintf(buffer, 7, "FINISH"); 108 | sendto(_socket, buffer, 6, 0, (struct sockaddr*) &broadcast_address, sizeof(broadcast_address)); 109 | } 110 | } 111 | 112 | } 113 | std::cout << "Ok: Process no longer be working" << std::endl; 114 | } 115 | 116 | 117 | } //namespace Sender 118 | -------------------------------------------------------------------------------- /src/Utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEBROADCASTER_UTILS_H 2 | #define FILEBROADCASTER_UTILS_H 3 | 4 | #if defined(_WIN32) || defined(_WIN64) 5 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 6 | #define addr_len int // 7 | #include // Windows 8 | #include // socket 9 | #pragma comment(lib, "Ws2_32.lib") // 10 | #else 11 | #define SOCKET int // 12 | #define SOCKADDR_IN sockaddr_in // 13 | #define addr_len socklen_t // Linux socket 14 | #include // 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include // memcpy 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "cxxopts.hpp" 28 | #include "Config.hpp" 29 | 30 | 31 | using namespace std::chrono_literals; 32 | 33 | 34 | namespace Utils { 35 | /** 36 | * Return a coded number 37 | * @param buffer - bytes array 38 | * @param count - bytes count 39 | */ 40 | size_t getNumberFromBytes(char* buffer, int count) { 41 | size_t number = 0; 42 | for (int i = 0; i < count; i++) { 43 | number = number << 8; 44 | number = number | (buffer[i] & 0xFF); 45 | } 46 | return number; 47 | } 48 | 49 | /** 50 | * Write a coded number 51 | * @param buffer - ptr to write 52 | * @param value - number 53 | * @param count - count bytes 54 | */ 55 | void writeBytesFromNumber(char* buffer, size_t number, int count) { 56 | for (int i = 0; i < count; i++) { 57 | buffer[count - i - 1] = (char) (number >> (i * 8)); 58 | } 59 | } 60 | } 61 | 62 | #endif //FILEBROADCASTER_UTILS_H 63 | -------------------------------------------------------------------------------- /tests/Tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../src/Utils.hpp" 4 | 5 | class BytesConverter : public ::testing::TestWithParam> { 6 | public: 7 | size_t value; 8 | int count; 9 | 10 | protected: 11 | void SetUp() override { 12 | std::tie(value, count) = GetParam(); 13 | } 14 | }; 15 | 16 | INSTANTIATE_TEST_CASE_P( 17 | CombinationsTest, BytesConverter, 18 | ::testing::Combine( 19 | ::testing::Values(0U, 1U, 2U, 1000U, 65535U, 2147483647U), 20 | ::testing::Values(4, 8, 16))); 21 | 22 | TEST_P(BytesConverter, getIntFromBytes) { 23 | char buffer[16] = { 0 }; 24 | 25 | Utils::writeBytesFromNumber(buffer, value, count); 26 | 27 | size_t result = Utils::getNumberFromBytes(buffer, count); 28 | 29 | EXPECT_EQ(result, value); 30 | } 31 | 32 | int main(int argc, char** argv) { 33 | testing::InitGoogleTest(&argc, argv); 34 | return RUN_ALL_TESTS(); 35 | } -------------------------------------------------------------------------------- /tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/tests.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6e714d4e-eab6-43b3-ad29-fb75743ca1bd} 23 | Win32Proj 24 | 10.0.17763.0 25 | Application 26 | v141 27 | Unicode 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | true 37 | 38 | 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | {cb24e1bf-ba1e-405a-809c-7ea285f3af1e} 47 | 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Use 62 | pch.h 63 | Disabled 64 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 65 | true 66 | EnableFastChecks 67 | MultiThreadedDebugDLL 68 | Level3 69 | 70 | 71 | true 72 | Console 73 | 74 | 75 | 76 | 77 | NotUsing 78 | 79 | 80 | Disabled 81 | X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 82 | true 83 | EnableFastChecks 84 | MultiThreadedDebugDLL 85 | Level3 86 | 87 | $(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories);$(SolutionDir)/lib/cxxopts/include/ 88 | 89 | 90 | true 91 | Console 92 | 93 | 94 | 95 | 96 | Use 97 | pch.h 98 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 99 | MultiThreadedDLL 100 | Level3 101 | ProgramDatabase 102 | 103 | 104 | true 105 | Console 106 | true 107 | true 108 | 109 | 110 | 111 | 112 | NotUsing 113 | 114 | 115 | X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 116 | MultiThreadedDLL 117 | Level3 118 | ProgramDatabase 119 | 120 | $(MSBuildThisFileDirectory)include;%(AdditionalIncludeDirectories);$(SolutionDir)/lib/cxxopts/include/ 121 | 122 | 123 | true 124 | Console 125 | true 126 | true 127 | 128 | 129 | 130 | 131 | Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}. 132 | 133 | 134 | 135 | --------------------------------------------------------------------------------