Networking Initial
This commit is contained in:
parent
df3767df29
commit
8e00882bb3
6 changed files with 178 additions and 86 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "Externals/asio"]
|
||||||
|
path = Externals/asio
|
||||||
|
url = https://github.com/IlyaShurupov/asio.git
|
||||||
1
Externals/asio
vendored
Submodule
1
Externals/asio
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d6b95c0188e0359a8cdbdb6571f0cbacf11a538c
|
||||||
|
|
@ -12,12 +12,6 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
|
||||||
|
|
||||||
### ---------------------- Applications --------------------- ###
|
|
||||||
add_executable(Server ./applications/Server.cpp)
|
|
||||||
add_executable(Client ./applications/Client.cpp)
|
|
||||||
target_link_libraries(Server PUBLIC ${PROJECT_NAME})
|
|
||||||
target_link_libraries(Client PUBLIC ${PROJECT_NAME})
|
|
||||||
|
|
||||||
### -------------------------- Tests -------------------------- ###
|
### -------------------------- Tests -------------------------- ###
|
||||||
enable_testing()
|
enable_testing()
|
||||||
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
||||||
|
|
@ -26,3 +20,7 @@ target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
|
||||||
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
|
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
|
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
|
||||||
|
|
||||||
|
|
||||||
|
# todo :remove
|
||||||
|
add_subdirectory(applications)
|
||||||
11
Storage/applications/CMakeLists.txt
Normal file
11
Storage/applications/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.2)
|
||||||
|
|
||||||
|
project(Applications)
|
||||||
|
|
||||||
|
### ---------------------- Applications --------------------- ###
|
||||||
|
add_executable(Server ./Server.cpp)
|
||||||
|
add_executable(Client ./Client.cpp)
|
||||||
|
|
||||||
|
include_directories(Client ./../../Externals/asio/asio/include)
|
||||||
|
include_directories(Server ./../../Externals/asio/asio/include)
|
||||||
|
|
@ -1,45 +1,81 @@
|
||||||
#include <iostream>
|
#include "asio.hpp"
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
constexpr int PORT = 8080;
|
#include <iostream>
|
||||||
constexpr int BUFFER_SIZE = 1024;
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
// #include <unistd.h>
|
||||||
|
// #include <cstring>
|
||||||
|
|
||||||
|
constexpr int PORT = 3333;
|
||||||
const char* SERVER_IP = "127.0.0.1";
|
const char* SERVER_IP = "127.0.0.1";
|
||||||
|
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
void* readServerBroadcast(void* clientSocketPtr) {
|
||||||
|
auto socket = ((asio::ip::tcp::socket*)clientSocketPtr);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
short messageSize;
|
||||||
|
asio::read(*socket, asio::buffer(&messageSize, 2));
|
||||||
|
|
||||||
|
mutex.lock();
|
||||||
|
|
||||||
|
auto message = new char[messageSize + 1];
|
||||||
|
message[messageSize] = 0;
|
||||||
|
|
||||||
|
asio::read(*socket, asio::buffer(message, messageSize));
|
||||||
|
|
||||||
|
std::cerr << "Broadcast : " << message << std::endl;
|
||||||
|
|
||||||
|
delete[] message;
|
||||||
|
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Create socket
|
asio::io_context ioContext;
|
||||||
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (clientSocket == -1) {
|
// Create a TCP socket
|
||||||
std::cerr << "Failed to create socket" << std::endl;
|
asio::ip::tcp::socket socket(ioContext);
|
||||||
|
|
||||||
|
// Connect to a server
|
||||||
|
asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string(SERVER_IP), PORT);
|
||||||
|
socket.connect(endpoint);
|
||||||
|
|
||||||
|
// Create a new thread to handle the client
|
||||||
|
pthread_t threadId;
|
||||||
|
if (pthread_create(&threadId, nullptr, readServerBroadcast, (void*) &socket) != 0) {
|
||||||
|
std::cerr << "Failed to create thread for client" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to server
|
// Detach the thread so it can run independently
|
||||||
sockaddr_in serverAddress{};
|
pthread_detach(threadId);
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
serverAddress.sin_port = htons(PORT);
|
|
||||||
if (inet_pton(AF_INET, SERVER_IP, &serverAddress.sin_addr) <= 0) {
|
|
||||||
std::cerr << "Invalid address or address not supported" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connect(clientSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) == -1) {
|
while (true) {
|
||||||
std::cerr << "Failed to connect to server" << std::endl;
|
std::string message;
|
||||||
return 1;
|
std::cout << " >> ";
|
||||||
}
|
std::cin >> message;
|
||||||
|
|
||||||
// Send message to server
|
mutex.lock();
|
||||||
const char* message = "Hello, server!";
|
|
||||||
if (write(clientSocket, message, strlen(message)) == -1) {
|
// Send a message to the server
|
||||||
std::cerr << "Failed to write to socket" << std::endl;
|
auto messageSize = (short) message.size();
|
||||||
return 1;
|
asio::write(socket, asio::buffer(&messageSize, 2));
|
||||||
|
|
||||||
|
// Send a message to the server
|
||||||
|
asio::write(socket, asio::buffer(message + "\n"));
|
||||||
|
|
||||||
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close socket
|
// Close socket
|
||||||
close(clientSocket);
|
// close(clientSocket);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,56 @@
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sys/socket.h>
|
#include <list>
|
||||||
#include <netinet/in.h>
|
#include <mutex>
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
|
|
||||||
|
struct SharedData {
|
||||||
|
std::list<asio::ip::tcp::socket*> clients;
|
||||||
|
std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
SharedData mSharedData;
|
||||||
|
|
||||||
|
//int serverSocket;
|
||||||
|
int port;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server(int port) : serverSocket(-1), port(port) {}
|
Server(int port) : port(port) {}
|
||||||
|
~Server() { assert(0); }
|
||||||
|
|
||||||
bool start() {
|
bool start() {
|
||||||
// Create socket
|
// Create socket
|
||||||
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
asio::io_context io_context;
|
||||||
if (serverSocket == -1) {
|
asio::ip::tcp::acceptor serverSocket(io_context);
|
||||||
std::cerr << "Failed to create socket" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind socket to port
|
// Bind socket to port
|
||||||
sockaddr_in serverAddress{};
|
serverSocket.open(asio::ip::tcp::v4());
|
||||||
serverAddress.sin_family = AF_INET;
|
// serverSocket.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
||||||
serverAddress.sin_port = htons(port);
|
serverSocket.bind(asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
|
||||||
serverAddress.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
if (bind(serverSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) == -1) {
|
|
||||||
std::cerr << "Failed to bind socket" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for connections
|
// Listen for connections
|
||||||
if (listen(serverSocket, 5) == -1) {
|
serverSocket.listen();
|
||||||
std::cerr << "Failed to listen on socket" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Server listening on port " << port << std::endl;
|
std::cout << "Server listening on port " << port << std::endl;
|
||||||
|
|
||||||
// Start accepting clients
|
// Start accepting clients
|
||||||
while (true) {
|
while (true) {
|
||||||
// Accept client connection
|
// Accept client connection
|
||||||
sockaddr_in clientAddress{};
|
auto clientSocket = new asio::ip::tcp::socket(io_context);
|
||||||
socklen_t clientAddressSize = sizeof(clientAddress);
|
serverSocket.accept(*clientSocket);
|
||||||
int clientSocket = accept(serverSocket, reinterpret_cast<sockaddr*>(&clientAddress), &clientAddressSize);
|
|
||||||
if (clientSocket == -1) {
|
// add client
|
||||||
std::cerr << "Failed to accept client connection" << std::endl;
|
mSharedData.mutex.lock();
|
||||||
return false;
|
mSharedData.clients.push_back(clientSocket);
|
||||||
}
|
mSharedData.mutex.unlock();
|
||||||
|
|
||||||
// Create a new thread to handle the client
|
// Create a new thread to handle the client
|
||||||
pthread_t threadId;
|
pthread_t threadId;
|
||||||
if (pthread_create(&threadId, nullptr, handleClient, &clientSocket) != 0) {
|
if (pthread_create(&threadId, nullptr, handleClient, this) != 0) {
|
||||||
std::cerr << "Failed to create thread for client" << std::endl;
|
std::cerr << "Failed to create thread for client" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -58,39 +60,80 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close server socket
|
// Close server socket
|
||||||
close(serverSocket);
|
serverSocket.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
int serverSocket;
|
|
||||||
int port;
|
|
||||||
|
|
||||||
static void* handleClient(void* clientSocketPtr) {
|
static void* handleClient(void* in) {
|
||||||
int clientSocket = *(reinterpret_cast<int*>(clientSocketPtr));
|
auto self = (Server*) in;
|
||||||
constexpr int BUFFER_SIZE = 1024;
|
|
||||||
|
|
||||||
// Receive and print client message
|
// read shared data - current client id
|
||||||
char buffer[BUFFER_SIZE];
|
self->mSharedData.mutex.lock();
|
||||||
ssize_t bytesRead = read(clientSocket, buffer, BUFFER_SIZE - 1);
|
auto clientSocket = self->mSharedData.clients.back();
|
||||||
if (bytesRead == -1) {
|
self->mSharedData.mutex.unlock();
|
||||||
std::cerr << "Failed to read from socket" << std::endl;
|
|
||||||
return nullptr;
|
MESSAGE:
|
||||||
|
// wait for a message request1
|
||||||
|
short messageSize;
|
||||||
|
{
|
||||||
|
auto bytesRead = asio::read(*clientSocket, asio::buffer(&messageSize, 2));
|
||||||
|
if (bytesRead == -1) {
|
||||||
|
std::cerr << "Failed to read from socket" << std::endl;
|
||||||
|
(*clientSocket).close();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer[bytesRead] = '\0';
|
self->mSharedData.mutex.lock();
|
||||||
std::cout << "Received message from client: " << buffer << std::endl;
|
|
||||||
|
// Receive client message
|
||||||
|
auto message = new char[messageSize + 1];
|
||||||
|
message[messageSize] = '\0';
|
||||||
|
{
|
||||||
|
auto bytesRead = asio::read(*clientSocket, asio::buffer(message, messageSize));
|
||||||
|
if (bytesRead == -1) {
|
||||||
|
std::cerr << "Failed to read from socket" << std::endl;
|
||||||
|
memcpy(message, "Cant wanna say something but i cant read", 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast to all clients
|
||||||
|
for (auto client: self->mSharedData.clients) {
|
||||||
|
auto bytesWritten = asio::write(*client, asio::buffer(&messageSize, 2));
|
||||||
|
if (bytesWritten == -1) {
|
||||||
|
std::cerr << "Failed to write to socket" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesWritten = write(*client, asio::buffer(message, messageSize));
|
||||||
|
if (bytesWritten == -1) {
|
||||||
|
std::cerr << "Failed to write to socket" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exit = memcmp(message, "exit", strlen("exit")) == 0;
|
||||||
|
|
||||||
// Close socket
|
// Close socket
|
||||||
close(clientSocket);
|
delete[] message;
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
|
if (exit) {
|
||||||
|
(*clientSocket).close();
|
||||||
|
self->mSharedData.clients.remove(clientSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->mSharedData.mutex.unlock();
|
||||||
|
|
||||||
|
if (exit) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
goto MESSAGE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Server server(8080);
|
Server server(3333);
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue