Networking Initial

This commit is contained in:
IlushaShurupov 2023-07-16 20:03:14 +03:00 committed by Ilya Shurupov
parent d5dfb1c304
commit 038e6c58cf
6 changed files with 178 additions and 86 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "Externals/asio"]
path = Externals/asio
url = https://github.com/IlyaShurupov/asio.git

1
Externals/asio vendored Submodule

@ -0,0 +1 @@
Subproject commit d6b95c0188e0359a8cdbdb6571f0cbacf11a538c

View file

@ -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")
@ -25,4 +19,8 @@ add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) 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)

View 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)

View file

@ -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;
} }

View file

@ -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,40 +60,81 @@ 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;
} }